Fail* directories reorganized, Code-cleanup (-> coding-style), Typos+comments fixed.
git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1321 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
This commit is contained in:
36
src/core/CMakeLists.txt
Normal file
36
src/core/CMakeLists.txt
Normal file
@ -0,0 +1,36 @@
|
||||
### Add Boost and Threads
|
||||
find_package(Boost 1.42 COMPONENTS thread REQUIRED)
|
||||
find_package(Threads)
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
link_directories(${Boost_LIBRARY_DIRS})
|
||||
|
||||
### Setup doxygen documentation
|
||||
# TODO: put into helpers.cmake
|
||||
find_package(Doxygen)
|
||||
if(DOXYGEN_FOUND)
|
||||
# Using a .in file means we can use CMake to insert project settings
|
||||
# into the doxyfile. For example, CMake will replace @PROJECT_NAME@ in
|
||||
# a configured file with the CMake PROJECT_NAME variable's value.
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY}
|
||||
)
|
||||
|
||||
## call make doc to generate documentation
|
||||
add_custom_target(doc
|
||||
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMENT "[${PROJECT_NAME}] Generating Fail* documentation with Doxygen" VERBATIM
|
||||
)
|
||||
endif(DOXYGEN_FOUND)
|
||||
|
||||
## Add CMakeLists from subdirectories ##
|
||||
# The autogenerated header files
|
||||
add_subdirectory(config)
|
||||
|
||||
# Fail* targets
|
||||
add_subdirectory(comm)
|
||||
add_subdirectory(cpn)
|
||||
add_subdirectory(efw)
|
||||
add_subdirectory(sal)
|
||||
add_subdirectory(util)
|
||||
1553
src/core/Doxyfile.in
Normal file
1553
src/core/Doxyfile.in
Normal file
File diff suppressed because it is too large
Load Diff
8
src/core/comm/CMakeLists.txt
Normal file
8
src/core/comm/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
set(SRCS
|
||||
SocketComm.cc
|
||||
)
|
||||
|
||||
add_subdirectory(msg)
|
||||
|
||||
add_library(comm ${SRCS})
|
||||
add_dependencies(comm msg)
|
||||
50
src/core/comm/ExperimentData.hpp
Normal file
50
src/core/comm/ExperimentData.hpp
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* \brief This is the base class for all user-defined data types for
|
||||
* expirement parameter and results.
|
||||
*/
|
||||
|
||||
#ifndef __EXPERIMENT_DATA_HPP__
|
||||
#define __EXPERIMENT_DATA_HPP__
|
||||
|
||||
#include <string>
|
||||
#include <google/protobuf/message.h>
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class ExperimentData
|
||||
* Container for experiment data with wrapper methods for serialization and deserialization.
|
||||
*/
|
||||
class ExperimentData {
|
||||
protected:
|
||||
google::protobuf::Message* msg;
|
||||
uint32_t m_workloadID;
|
||||
public:
|
||||
ExperimentData() : msg(0), m_workloadID(0) {};
|
||||
ExperimentData(google::protobuf::Message* m) : msg(m) , m_workloadID(0) { }
|
||||
|
||||
google::protobuf::Message& getMessage() { return *msg; }
|
||||
uint32_t getWorkloadID() const { return m_workloadID; };
|
||||
void setWorkloadID(uint32_t id) { m_workloadID = id; }
|
||||
/**
|
||||
* Serializes the ExperimentData.
|
||||
* @param ped output the target-stream.
|
||||
* @return \c true if the serialization was successful, \c false otherwise
|
||||
*/
|
||||
bool serialize(std::ostream* output) const { return msg->SerializeToOstream(output); }
|
||||
/**
|
||||
* Unserializes the ExperimentData.
|
||||
* @param ped input the stream which is read from
|
||||
* @return \c true if the unserialization was successful, \c false otherwise
|
||||
*/
|
||||
bool unserialize(std::istream* input) { return msg->ParseFromIstream(input); }
|
||||
/**
|
||||
* Returns a debug string.
|
||||
* @return the debug string
|
||||
*/
|
||||
std::string debugString() const { return msg->DebugString(); };
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif //__EXPERIMENT_DATA_HPP__
|
||||
52
src/core/comm/SocketComm.cc
Normal file
52
src/core/comm/SocketComm.cc
Normal file
@ -0,0 +1,52 @@
|
||||
#include <string>
|
||||
|
||||
#include "SocketComm.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
bool SocketComm::sendMsg(int sockfd, google::protobuf::Message& msg)
|
||||
{
|
||||
#ifdef USE_SIZE_PREFIX
|
||||
int size = htonl(msg.ByteSize());
|
||||
if (write(sockfd, &size, sizeof(size)) != sizeof(size)) {
|
||||
return false;
|
||||
}
|
||||
std::string buf;
|
||||
msg.SerializeToString(&buf);
|
||||
if (write(sockfd, buf.c_str(), buf.size()) != (ssize_t) buf.size()) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
char c = 0;
|
||||
if (!msg.SerializeToFileDescriptor(sockfd) || write(sockfd, &c, 1) != 1) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SocketComm::rcvMsg(int sockfd, google::protobuf::Message& msg)
|
||||
{
|
||||
#ifdef USE_SIZE_PREFIX
|
||||
int size;
|
||||
// FIXME: read() may need to be called multiple times until all data was read
|
||||
if (read(sockfd, &size, sizeof(size)) != sizeof(size)) {
|
||||
return false;
|
||||
}
|
||||
size = ntohl(size);
|
||||
char *buf = new char[size];
|
||||
// FIXME: read() may need to be called multiple times until all data was read
|
||||
if (read(sockfd, buf, size) != size) {
|
||||
delete [] buf;
|
||||
return false;
|
||||
}
|
||||
std::string st(buf, size);
|
||||
delete [] buf;
|
||||
msg.ParseFromString(st);
|
||||
return true;
|
||||
#else
|
||||
return msg.ParseFromFileDescriptor(sockfd);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // end-of-namespace: fail
|
||||
41
src/core/comm/SocketComm.hpp
Normal file
41
src/core/comm/SocketComm.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* \brief Socket based communictaion wrapper functions.
|
||||
*/
|
||||
|
||||
#ifndef __SOCKET_COMM_HPP__
|
||||
#define __SOCKET_COMM_HPP__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <google/protobuf/message.h>
|
||||
|
||||
#define USE_SIZE_PREFIX
|
||||
|
||||
namespace fail {
|
||||
|
||||
class SocketComm {
|
||||
public:
|
||||
/**
|
||||
* Send Protobuf-generated message
|
||||
* @param sockfd open socket descriptor to write to
|
||||
* @param Msg Reference to Protobuf generated message type
|
||||
* \return false if message sending failed
|
||||
*/
|
||||
static bool sendMsg(int sockfd, google::protobuf::Message& msg);
|
||||
/**
|
||||
* Receive Protobuf-generated message
|
||||
* @param sockfd open socket descriptor to read from
|
||||
* @param Msg Reference to Protobuf generated message type
|
||||
* \return false if message reception failed
|
||||
*/
|
||||
static bool rcvMsg(int sockfd, google::protobuf::Message& msg);
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __SOCKET_COMM_HPP__
|
||||
15
src/core/comm/msg/CMakeLists.txt
Normal file
15
src/core/comm/msg/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
## Setup desired protobuf descriptions HERE ##
|
||||
set(MY_PROTOS
|
||||
FailControlMessage.proto
|
||||
)
|
||||
|
||||
#### PROTOBUFS ####
|
||||
find_package(Protobuf REQUIRED)
|
||||
include_directories(${PROTOBUF_INCLUDE_DIRS})
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
PROTOBUF_GENERATE_CPP(PROTO_SRCS PROTO_HDRS ${MY_PROTOS})
|
||||
|
||||
## Build library
|
||||
add_library(msg ${PROTO_SRCS} ${PROTO_HDRS})
|
||||
|
||||
16
src/core/comm/msg/FailControlMessage.proto
Normal file
16
src/core/comm/msg/FailControlMessage.proto
Normal file
@ -0,0 +1,16 @@
|
||||
message FailControlMessage {
|
||||
enum Command {
|
||||
// Minions may send these:
|
||||
NEED_WORK = 0; // server replies with WORK_FOLLOWS or DIE
|
||||
RESULT_FOLLOWS = 1; // followed by experiment-specific ExperimentData message (holding both original parameters and experiment result)
|
||||
|
||||
// JobServer may send these:
|
||||
WORK_FOLLOWS = 5; // followed by experiment-specific ExperimentData message
|
||||
COME_AGAIN = 6; // no experiment-specific ExperimentData at the moment, but Campaign is not over yet
|
||||
DIE = 7; // tells the client to terminate
|
||||
}
|
||||
|
||||
required Command command = 1;
|
||||
optional uint32 workloadID = 2;
|
||||
required uint64 build_id = 3; // identifying the client/server build (e.g., build time in unixtime format)
|
||||
}
|
||||
2
src/core/comm/msg/protogen.sh
Executable file
2
src/core/comm/msg/protogen.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
protoc --cpp_out=. FailControlMessage.proto
|
||||
28
src/core/config/CMakeLists.txt
Normal file
28
src/core/config/CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
||||
#### Configuration file emitting BUILD_OVP/BOCHS defines ####
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/VariantConfig.hpp.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/VariantConfig.hpp)
|
||||
|
||||
OPTION(CONFIG_EVENT_BREAKPOINTS "Event source: Breakpoints" OFF)
|
||||
OPTION(CONFIG_EVENT_MEMREAD "Event source: Memory reads" OFF)
|
||||
OPTION(CONFIG_EVENT_MEMWRITE "Event source: Memory writes" OFF)
|
||||
OPTION(CONFIG_EVENT_GUESTSYS "Event source: Outbound guest-system communication" OFF)
|
||||
OPTION(CONFIG_EVENT_IOPORT "Event source: I/O port communication" OFF)
|
||||
OPTION(CONFIG_EVENT_INTERRUPT "Event source: Interrupts" OFF)
|
||||
OPTION(CONFIG_EVENT_TRAP "Event source: Traps" OFF)
|
||||
OPTION(CONFIG_EVENT_JUMP "Event source: Branch instructions" OFF)
|
||||
OPTION(CONFIG_SR_RESTORE "Target backend: State restore" OFF)
|
||||
OPTION(CONFIG_SR_SAVE "Target backend: State saving" OFF)
|
||||
OPTION(CONFIG_SR_REBOOT "Target backend: Reboot" OFF)
|
||||
OPTION(CONFIG_BOCHS_NON_VERBOSE "Misc: Reduced verbosity" OFF)
|
||||
OPTION(CONFIG_SUPPRESS_INTERRUPTS "Target backend: Suppress interrupts" OFF)
|
||||
OPTION(CONFIG_FIRE_INTERRUPTS "Target backend: Fire interrupts" OFF)
|
||||
OPTION(CONFIG_DISABLE_KEYB_INTERRUPTS "Target backend: Suppress keyboard interrupts" OFF)
|
||||
OPTION(SERVER_PERFORMANCE_MEASURE "Performance measurement in job-server" OFF)
|
||||
SET(SERVER_PERF_LOG_PATH "perf.log" CACHE STRING "A file name for storing the server's performance log (CSV)")
|
||||
SET(SERVER_PERF_STEPPING_SEC "1" CACHE STRING "Stepping of performance measurements in seconds")
|
||||
SET(CLIENT_RAND_BACKOFF_TSTART "3" CACHE STRING "Lower limit of client's backoff phase in seconds")
|
||||
SET(CLIENT_RAND_BACKOFF_TEND "8" CACHE STRING "Upper limit of client's backoff phase in seconds")
|
||||
SET(CLIENT_RETRY_COUNT "3" CACHE STRING "Client's number of reconnect retries")
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/FailConfig.hpp.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/FailConfig.hpp)
|
||||
35
src/core/config/FailConfig.hpp.in
Normal file
35
src/core/config/FailConfig.hpp.in
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef __FAIL_CONFIG_HPP__
|
||||
#define __FAIL_CONFIG_HPP__
|
||||
|
||||
// #define / #undef the following configuration macros to enable/disable the
|
||||
// various event sources, fault injection sinks, and miscellaneous other
|
||||
// features.
|
||||
|
||||
// Event sources
|
||||
#cmakedefine CONFIG_EVENT_BREAKPOINTS
|
||||
#cmakedefine CONFIG_EVENT_MEMREAD
|
||||
#cmakedefine CONFIG_EVENT_MEMWRITE
|
||||
#cmakedefine CONFIG_EVENT_GUESTSYS
|
||||
#cmakedefine CONFIG_EVENT_IOPORT
|
||||
#cmakedefine CONFIG_EVENT_INTERRUPT
|
||||
#cmakedefine CONFIG_EVENT_TRAP
|
||||
#cmakedefine CONFIG_EVENT_JUMP
|
||||
|
||||
// Save/restore functionality
|
||||
#cmakedefine CONFIG_SR_RESTORE
|
||||
#cmakedefine CONFIG_SR_SAVE
|
||||
#cmakedefine CONFIG_SR_REBOOT
|
||||
|
||||
// Fail configuration
|
||||
#cmakedefine CONFIG_BOCHS_NON_VERBOSE
|
||||
#cmakedefine CONFIG_SUPPRESS_INTERRUPTS
|
||||
#cmakedefine CONFIG_FIRE_INTERRUPTS
|
||||
#cmakedefine CONFIG_DISABLE_KEYB_INTERRUPTS
|
||||
#cmakedefine SERVER_PERFORMANCE_MEASURE
|
||||
#cmakedefine SERVER_PERF_LOG_PATH "@SERVER_PERF_LOG_PATH@"
|
||||
#cmakedefine SERVER_PERF_STEPPING_SEC @SERVER_PERF_STEPPING_SEC@
|
||||
#cmakedefine CLIENT_RAND_BACKOFF_TSTART @CLIENT_RAND_BACKOFF_TSTART@
|
||||
#cmakedefine CLIENT_RAND_BACKOFF_TEND @CLIENT_RAND_BACKOFF_TEND@
|
||||
#cmakedefine CLIENT_RETRY_COUNT @CLIENT_RETRY_COUNT@
|
||||
|
||||
#endif // __FAIL_CONFIG_HPP__
|
||||
7
src/core/config/VariantConfig.hpp.in
Normal file
7
src/core/config/VariantConfig.hpp.in
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef __VARIANT_CONFIG_HPP__
|
||||
#define __VARIANT_CONFIG_HPP__
|
||||
|
||||
#cmakedefine BUILD_OVP
|
||||
#cmakedefine BUILD_BOCHS
|
||||
|
||||
#endif // __VARIANT_CONFIG_HPP__
|
||||
8
src/core/cpn/CMakeLists.txt
Normal file
8
src/core/cpn/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
set(SRCS
|
||||
CampaignManager.cc
|
||||
JobServer.cc
|
||||
)
|
||||
|
||||
add_library(cpn ${SRCS})
|
||||
|
||||
add_dependencies(cpn comm)
|
||||
25
src/core/cpn/Campaign.hpp
Normal file
25
src/core/cpn/Campaign.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef __CAMPAIGN_HPP__
|
||||
#define __CAMPAIGN_HPP__
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class Campaign
|
||||
*
|
||||
* Basic interface for user-defined campaigns. To create a new
|
||||
* campaign, derive your own class from Campaign,
|
||||
* define the run method, and add it to the CampaignManager.
|
||||
*/
|
||||
class Campaign {
|
||||
public:
|
||||
Campaign() { }
|
||||
/**
|
||||
* Defines the campaign.
|
||||
* @return \c true if the campaign was successful, \c false otherwise
|
||||
*/
|
||||
virtual bool run() = 0;
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __CAMPAIGN_HPP__
|
||||
7
src/core/cpn/CampaignManager.cc
Normal file
7
src/core/cpn/CampaignManager.cc
Normal file
@ -0,0 +1,7 @@
|
||||
#include "CampaignManager.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
CampaignManager campaignmanager;
|
||||
|
||||
} // end-of-namespace: fail
|
||||
77
src/core/cpn/CampaignManager.hpp
Normal file
77
src/core/cpn/CampaignManager.hpp
Normal file
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* \brief The manager for an entire campaign
|
||||
*/
|
||||
|
||||
#ifndef __CAMPAIGN_MANAGER_HPP__
|
||||
#define __CAMPAIGN_MANAGER_HPP__
|
||||
|
||||
#include "sal/SALInst.hpp"
|
||||
#include "comm/ExperimentData.hpp"
|
||||
#include "JobServer.hpp"
|
||||
#include "Campaign.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class CampaignManager
|
||||
*
|
||||
* The CampaignManager allows a user-campaign access to all constant
|
||||
* simulator information and forwards single experiments to the JobServer.
|
||||
*/
|
||||
class CampaignManager {
|
||||
private:
|
||||
JobServer m_jobserver;
|
||||
Campaign* m_currentCampaign;
|
||||
public:
|
||||
CampaignManager() { }
|
||||
/**
|
||||
* Executes a user campaign
|
||||
*/
|
||||
bool runCampaign(Campaign* c)
|
||||
{
|
||||
m_currentCampaign = c;
|
||||
bool ret = c->run();
|
||||
m_jobserver.done();
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Returns a const reference for acquiring constant simulator specific information.
|
||||
* e.g., Registernames, to ease experiment data construction.
|
||||
* The campaign description is not allowed to change the simulator
|
||||
* state, as the actual simulation runs within another process (Minion)
|
||||
* @return constant reference to the current simulator backend.
|
||||
*/
|
||||
SimulatorController const& getSimulator() const { return simulator; }
|
||||
/**
|
||||
* Add a experiment parameter set.
|
||||
* The user campaign has to allocate the Parameter object,
|
||||
* and deallocate it after result reception.
|
||||
* A Parameter set includes space for results.
|
||||
* @param exp A pointer to a ExperimentData set.
|
||||
*/
|
||||
void addParam(ExperimentData* exp) { m_jobserver.addParam(exp); }
|
||||
/**
|
||||
* A user campaign can request a single result (blocking) from the queue.
|
||||
* @return Pointer to a parameter object with filled result data
|
||||
* @see addParam()
|
||||
*/
|
||||
ExperimentData* getDone() { return m_jobserver.getDone(); }
|
||||
/**
|
||||
* Signal, that there will not come any further parameter sets.
|
||||
*/
|
||||
void noMoreParameters() { m_jobserver.setNoMoreExperiments(); }
|
||||
/**
|
||||
* User campaign has finished.
|
||||
*/
|
||||
void done() { m_jobserver.done(); }
|
||||
/**
|
||||
* Wait actively, until all experiments expired.
|
||||
*/
|
||||
// void waitForCompletion();
|
||||
};
|
||||
|
||||
extern CampaignManager campaignmanager;
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __CAMPAIGN_MANAGER_HPP__
|
||||
313
src/core/cpn/JobServer.cc
Normal file
313
src/core/cpn/JobServer.cc
Normal file
@ -0,0 +1,313 @@
|
||||
// <iostream> needs to be included before *.pb.h, otherwise ac++/Puma chokes on the latter
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <strings.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "comm/msg/FailControlMessage.pb.h"
|
||||
#include "comm/SocketComm.hpp"
|
||||
#include "JobServer.hpp"
|
||||
#include "Minion.hpp"
|
||||
|
||||
#ifndef __puma
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/date_time.hpp>
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace fail {
|
||||
|
||||
void JobServer::addParam(ExperimentData* exp)
|
||||
{
|
||||
#ifndef __puma
|
||||
m_undoneJobs.Enqueue(exp);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SERVER_PERFORMANCE_MEASURE
|
||||
volatile unsigned JobServer::m_DoneCount = 0;
|
||||
#endif
|
||||
|
||||
ExperimentData *JobServer::getDone()
|
||||
{
|
||||
// FIXME race condition, need to synchronize with
|
||||
// sendPendingExperimentData() and receiveExperimentResults()
|
||||
#ifndef __puma
|
||||
if (m_undoneJobs.Size() == 0
|
||||
&& noMoreExperiments()
|
||||
&& m_runningJobs.Size() == 0
|
||||
&& m_doneJobs.Size() == 0) {
|
||||
// FRICKEL workaround
|
||||
sleep(1);
|
||||
ExperimentData *exp = NULL;
|
||||
if (m_doneJobs.Dequeue_nb(exp)) {
|
||||
return exp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return m_doneJobs.Dequeue();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef SERVER_PERFORMANCE_MEASURE
|
||||
void JobServer::measure()
|
||||
{
|
||||
// TODO: Log-level?
|
||||
cout << "\n[Server] Logging throughput in \"" << SERVER_PERF_LOG_PATH << "\"..." << endl;
|
||||
ofstream m_file(SERVER_PERF_LOG_PATH, std::ios::trunc); // overwrite existing perf-logs
|
||||
if (!m_file.is_open()) {
|
||||
cerr << "[Server] Perf-logging has been enabled"
|
||||
<< "but I was not able to write the log-file \""
|
||||
<< SERVER_PERF_LOG_PATH << "\"." << endl;
|
||||
exit(1);
|
||||
}
|
||||
unsigned counter = 0;
|
||||
|
||||
m_file << "time\tthroughput" << endl;
|
||||
unsigned diff = 0;
|
||||
while (!m_finish) {
|
||||
// Format: 1st column (seconds)[TAB]2nd column (throughput)
|
||||
m_file << counter << "\t" << (m_DoneCount - diff) << endl;
|
||||
counter += SERVER_PERF_STEPPING_SEC;
|
||||
diff = m_DoneCount;
|
||||
sleep(SERVER_PERF_STEPPING_SEC);
|
||||
}
|
||||
// NOTE: Summing up the values written in the 2nd column does not
|
||||
// necessarily yield the number of completed experiments/jobs
|
||||
// (due to thread-scheduling behaviour -> not sync'd!)
|
||||
}
|
||||
#endif // SERVER_PERFORMANCE_MEASURE
|
||||
|
||||
#ifndef __puma
|
||||
/**
|
||||
* This is a predicate class for the remove_if operator on the thread
|
||||
* list. The operator waits for timeout milliseconds to join each
|
||||
* thread in the list. If the join was successful, the exited thread
|
||||
* is deallocated and removed from the list.
|
||||
*/
|
||||
struct timed_join_successful {
|
||||
int timeout_ms;
|
||||
timed_join_successful(int timeout)
|
||||
: timeout_ms(timeout) { }
|
||||
|
||||
bool operator()(boost::thread* threadelement)
|
||||
{
|
||||
boost::posix_time::time_duration timeout = boost::posix_time::milliseconds(timeout_ms);
|
||||
if (threadelement->timed_join(timeout)) {
|
||||
delete threadelement;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
void JobServer::run()
|
||||
{
|
||||
struct sockaddr_in clientaddr;
|
||||
socklen_t clen = sizeof(clientaddr);
|
||||
|
||||
// implementation of server-client communication
|
||||
int s;
|
||||
if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
|
||||
perror("socket");
|
||||
// TODO: Log-level?
|
||||
return;
|
||||
}
|
||||
|
||||
/* Enable address reuse */
|
||||
int on = 1;
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
||||
perror("setsockopt");
|
||||
// TODO: Log-level?
|
||||
return;
|
||||
}
|
||||
|
||||
/* IPv4, Port: 1111, accept all IP adresses */
|
||||
struct sockaddr_in saddr;
|
||||
saddr.sin_family = AF_INET;
|
||||
saddr.sin_port = htons(m_port);
|
||||
saddr.sin_addr.s_addr = htons(INADDR_ANY);
|
||||
|
||||
/* bind to port */
|
||||
if (bind(s, (struct sockaddr*) &saddr, sizeof(saddr)) == -1) {
|
||||
perror("bind");
|
||||
// TODO: Log-level?
|
||||
return;
|
||||
}
|
||||
|
||||
/* Listen with a backlog of maxThreads */
|
||||
if (listen(s, m_maxThreads) == -1) {
|
||||
perror("listen");
|
||||
// TODO: Log-level?
|
||||
return;
|
||||
}
|
||||
cout << "JobServer listening...." << endl;
|
||||
// TODO: Log-level?
|
||||
#ifndef __puma
|
||||
boost::thread* th;
|
||||
while(!m_finish){
|
||||
// Accept connection
|
||||
int cs = accept(s, (struct sockaddr*)&clientaddr, &clen);
|
||||
if (cs == -1) {
|
||||
perror("accept");
|
||||
// TODO: Log-level?
|
||||
return;
|
||||
}
|
||||
// Spawn a thread for further communication,
|
||||
// and add this thread to a list threads
|
||||
// We can limit the generation of threads here.
|
||||
if (m_threadlist.size() < m_maxThreads) {
|
||||
th = new boost::thread(CommThread(cs, *this));
|
||||
m_threadlist.push_back(th);
|
||||
} else {
|
||||
// Run over list with a timed_join,
|
||||
// removing finished threads.
|
||||
do {
|
||||
m_threadlist.remove_if(timed_join_successful(m_threadtimeout));
|
||||
} while(m_threadlist.size() == m_maxThreads);
|
||||
// Start new thread
|
||||
th = new boost::thread(CommThread(cs, *this));
|
||||
m_threadlist.push_back(th);
|
||||
}
|
||||
|
||||
}
|
||||
close(s);
|
||||
// when all undone Jobs are distributed -> call a timed_join on all spawned
|
||||
// TODO: interrupt threads that do not want to join..
|
||||
while (m_threadlist.size() > 0)
|
||||
m_threadlist.remove_if( timed_join_successful(m_threadtimeout) );
|
||||
#endif
|
||||
}
|
||||
|
||||
void CommThread::operator()()
|
||||
{
|
||||
// The communication thread implementation:
|
||||
|
||||
Minion minion;
|
||||
FailControlMessage ctrlmsg;
|
||||
minion.setSocketDescriptor(m_sock);
|
||||
|
||||
if (!SocketComm::rcvMsg(minion.getSocketDescriptor(), ctrlmsg)) {
|
||||
cout << "!![Server] failed to read complete message from client" << endl;
|
||||
close(m_sock);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (ctrlmsg.command()) {
|
||||
case FailControlMessage_Command_NEED_WORK:
|
||||
// give minion something to do..
|
||||
sendPendingExperimentData(minion);
|
||||
break;
|
||||
case FailControlMessage_Command_RESULT_FOLLOWS:
|
||||
// get results and put to done queue.
|
||||
receiveExperimentResults(minion, ctrlmsg.workloadid());
|
||||
break;
|
||||
default:
|
||||
// hm.. don't know what to do. please die.
|
||||
cout << "!![Server] no idea what to do with command #"
|
||||
<< ctrlmsg.command() << ", telling minion to die." << endl;
|
||||
ctrlmsg.Clear();
|
||||
ctrlmsg.set_command(FailControlMessage_Command_DIE);
|
||||
ctrlmsg.set_build_id(42);
|
||||
SocketComm::sendMsg(minion.getSocketDescriptor(), ctrlmsg);
|
||||
}
|
||||
|
||||
close(m_sock);
|
||||
}
|
||||
|
||||
#ifndef __puma
|
||||
boost::mutex CommThread::m_CommMutex;
|
||||
#endif // __puma
|
||||
|
||||
void CommThread::sendPendingExperimentData(Minion& minion)
|
||||
{
|
||||
FailControlMessage ctrlmsg;
|
||||
ctrlmsg.set_build_id(42);
|
||||
ExperimentData * exp = 0;
|
||||
if (m_js.m_undoneJobs.Dequeue_nb(exp) == true) {
|
||||
// Got an element from queue, assign ID to workload and send to minion
|
||||
uint32_t workloadID = m_js.m_counter.increment(); // increment workload counter
|
||||
exp->setWorkloadID(workloadID); // store ID for identification when receiving result
|
||||
if (!m_js.m_runningJobs.insert(workloadID, exp)) {
|
||||
cout << "!![Server]could not insert workload id: [" << workloadID << "] double entry?" << endl;
|
||||
}
|
||||
ctrlmsg.set_command(FailControlMessage_Command_WORK_FOLLOWS);
|
||||
ctrlmsg.set_workloadid(workloadID); // set workload id
|
||||
//cout << ">>[Server] Sending workload [" << workloadID << "]" << endl;
|
||||
cout << ">>[" << workloadID << "] " << flush;
|
||||
SocketComm::sendMsg(minion.getSocketDescriptor(), ctrlmsg);
|
||||
SocketComm::sendMsg(minion.getSocketDescriptor(), exp->getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef __puma
|
||||
boost::unique_lock<boost::mutex> lock(m_CommMutex);
|
||||
#endif
|
||||
if ((exp = m_js.m_runningJobs.pickone()) != NULL) { // 2nd priority
|
||||
// (This picks one running job.)
|
||||
// TODO: Improve selection of parameter set to be resent:
|
||||
// - currently: Linear complexity!
|
||||
// - pick entry at random?
|
||||
// - retry counter for each job?
|
||||
|
||||
// Implement resend of running-parameter sets to improve campaign speed
|
||||
// and to prevent result loss due to (unexpected) termination of experiment
|
||||
// clients.
|
||||
// (Note: Therefore we need to be aware of receiving multiple results for a
|
||||
// single parameter-set, @see receiveExperimentResults.)
|
||||
uint32_t workloadID = exp->getWorkloadID(); // (this ID has been set previously)
|
||||
// Resend the parameter-set.
|
||||
ctrlmsg.set_command(FailControlMessage_Command_WORK_FOLLOWS);
|
||||
ctrlmsg.set_workloadid(workloadID); // set workload id
|
||||
//cout << ">>[Server] Re-sending workload [" << workloadID << "]" << endl;
|
||||
cout << ">>R[" << workloadID << "] " << flush;
|
||||
SocketComm::sendMsg(minion.getSocketDescriptor(), ctrlmsg);
|
||||
SocketComm::sendMsg(minion.getSocketDescriptor(), exp->getMessage());
|
||||
} else if (m_js.noMoreExperiments() == false) {
|
||||
// Currently we have no workload (even the running-job-queue is empty!), but
|
||||
// the campaign is not over yet. Minion can try again later.
|
||||
ctrlmsg.set_command(FailControlMessage_Command_COME_AGAIN);
|
||||
SocketComm::sendMsg(minion.getSocketDescriptor(), ctrlmsg);
|
||||
cout << "--[Server] No workload, come again..." << endl;
|
||||
} else {
|
||||
// No more elements, and campaign is over. Minion can die.
|
||||
ctrlmsg.set_command(FailControlMessage_Command_DIE);
|
||||
cout << "--[Server] No workload, and no campaign, please die." << endl;
|
||||
SocketComm::sendMsg(minion.getSocketDescriptor(), ctrlmsg);
|
||||
}
|
||||
}
|
||||
|
||||
void CommThread::receiveExperimentResults(Minion& minion, uint32_t workloadID)
|
||||
{
|
||||
#ifndef __puma
|
||||
boost::unique_lock<boost::mutex> lock(m_CommMutex);
|
||||
#endif
|
||||
|
||||
ExperimentData * exp; // Get exp* from running jobs
|
||||
//cout << "<<[Server] Received result for workload id [" << workloadID << "]" << endl;
|
||||
cout << "<<[" << workloadID << "] " << flush;
|
||||
if (m_js.m_runningJobs.remove(workloadID, exp)) { // ExperimentData* found
|
||||
SocketComm::rcvMsg(minion.getSocketDescriptor(), exp->getMessage() ); // deserialize results.
|
||||
m_js.m_doneJobs.Enqueue(exp); // Put results in done queue..
|
||||
#ifdef SERVER_PERFORMANCE_MEASURE
|
||||
++JobServer::m_DoneCount;
|
||||
#endif
|
||||
} else {
|
||||
// We can receive several results for the same workload id because
|
||||
// we (may) distribute the (running) jobs to a *few* experiment-clients.
|
||||
cout << "[Server] Received another result for workload id ["
|
||||
<< workloadID << "] -- ignored." << endl;
|
||||
|
||||
// TODO: Any need for error-handling here?
|
||||
}
|
||||
}
|
||||
|
||||
} // end-of-namespace: fail
|
||||
168
src/core/cpn/JobServer.hpp
Normal file
168
src/core/cpn/JobServer.hpp
Normal file
@ -0,0 +1,168 @@
|
||||
#ifndef __JOB_SERVER_H__
|
||||
#define __JOB_SERVER_H__
|
||||
|
||||
#include "Minion.hpp"
|
||||
#include "util/SynchronizedQueue.hpp"
|
||||
#include "util/SynchronizedCounter.hpp"
|
||||
#include "util/SynchronizedMap.hpp"
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#include <list>
|
||||
|
||||
#ifndef __puma
|
||||
#include <boost/thread.hpp>
|
||||
#endif
|
||||
|
||||
namespace fail {
|
||||
|
||||
class CommThread;
|
||||
|
||||
/**
|
||||
* \class JobServer
|
||||
* The server supplies the Minions with ExperimentData's and receives the result data.
|
||||
*
|
||||
* Manages the campaigns parameter distributions. The Campaign Controller can add
|
||||
* experiment parameter sets, which the Jobserver will distribute to requesting
|
||||
* clients. The campaign controller can wait for all results, or a timeout.
|
||||
*/
|
||||
class JobServer {
|
||||
private:
|
||||
//! The TCP Port number
|
||||
int m_port;
|
||||
//! TODO nice termination concept
|
||||
bool m_finish;
|
||||
//! Campaign signaled last expirement data set
|
||||
bool m_noMoreExps;
|
||||
//! the maximal number of threads spawned for TCP communication
|
||||
unsigned m_maxThreads;
|
||||
//! the maximal timeout per communication thread
|
||||
int m_threadtimeout;
|
||||
//! A of spawned threads
|
||||
#ifndef __puma
|
||||
typedef std::list<boost::thread*> Tthreadlist;
|
||||
Tthreadlist m_threadlist;
|
||||
|
||||
boost::thread* m_serverThread;
|
||||
#endif // puma
|
||||
|
||||
#ifdef SERVER_PERFORMANCE_MEASURE
|
||||
static volatile unsigned m_DoneCount; //! the number of finished jobs
|
||||
#ifndef __puma
|
||||
boost::thread* m_measureThread; //! the performance measurement thread
|
||||
#endif
|
||||
#endif
|
||||
//! Atomic counter for Workload IDs.
|
||||
SynchronizedCounter m_counter;
|
||||
//! Map of running jobs (referenced by Workload ID
|
||||
SynchronizedMap<uint32_t, ExperimentData*> m_runningJobs;
|
||||
//! List of undone jobs, here the campaigns jobs enter
|
||||
SynchronizedQueue<ExperimentData*> m_undoneJobs;
|
||||
//! List of finished experiment results.
|
||||
SynchronizedQueue<ExperimentData*> m_doneJobs;
|
||||
friend class CommThread; //!< CommThread is allowed access the job queues.
|
||||
/**
|
||||
* The actual startup of the Jobserver.
|
||||
* Here we initalize the network socket
|
||||
* and listen for connections.
|
||||
*/
|
||||
void run();
|
||||
#ifdef SERVER_PERFORMANCE_MEASURE
|
||||
void measure();
|
||||
#endif
|
||||
void sendWork(int sockfd);
|
||||
|
||||
public:
|
||||
JobServer(int port = 1111) : m_port(port), m_finish(false), m_noMoreExps(false),
|
||||
m_maxThreads(128), m_threadtimeout(0)
|
||||
{
|
||||
#ifndef __puma
|
||||
m_serverThread = new boost::thread(&JobServer::run, this); // run operator()() in a thread.
|
||||
#ifdef SERVER_PERFORMANCE_MEASURE
|
||||
m_measureThread = new boost::thread(&JobServer::measure, this);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
~JobServer()
|
||||
{
|
||||
#ifndef __puma
|
||||
// Cleanup of m_serverThread, etc.
|
||||
delete m_serverThread;
|
||||
#ifdef SERVER_PERFORMANCE_MEASURE
|
||||
delete m_measureThread;
|
||||
#endif
|
||||
#endif // __puma
|
||||
}
|
||||
/**
|
||||
* Adds a new experiment data set to the job queue.
|
||||
* @param data Pointer to an expoeriment data object
|
||||
*/
|
||||
void addParam(ExperimentData* data);
|
||||
/**
|
||||
* Retrieve an experiment result. Blocks if we currently have no results.
|
||||
* Returns \c NULL if no results are to be expected, because no parameter
|
||||
* sets were enqueued beforehand.
|
||||
* @return pointer to experiment result data
|
||||
*/
|
||||
ExperimentData* getDone();
|
||||
/**
|
||||
* The Campaign controller must signalize, that there will be no
|
||||
* more parameter sets. We need this, as we allow concurrent parameter
|
||||
* generation and distribution.
|
||||
*/
|
||||
void setNoMoreExperiments() { m_noMoreExps = true; }
|
||||
/**
|
||||
* Checks whether there are no more experiment paremeter sets.
|
||||
* @return \c true if no more parameter sets available, \c false otherwise
|
||||
* @see setNoMoreExperiments
|
||||
*/
|
||||
bool noMoreExperiments() const { return m_noMoreExps; }
|
||||
|
||||
/**
|
||||
* The Campaign Controller can signalize, that the jobserver can
|
||||
* stop listening for client connections.
|
||||
*/
|
||||
void done() { m_finish = true; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @class CommThread
|
||||
* Implementation of the communication threads.
|
||||
* This class implements the actual communication
|
||||
* with the minions.
|
||||
*/
|
||||
class CommThread {
|
||||
private:
|
||||
int m_sock; //! Socket descriptor of the connection
|
||||
JobServer& m_js; //! Calling jobserver
|
||||
#ifndef __puma
|
||||
static boost::mutex m_CommMutex; //! to synchronise the communication
|
||||
#endif // __puma
|
||||
|
||||
// FIXME: Concerns are not really separated yet ;)
|
||||
/**
|
||||
* Called after minion calls for work.
|
||||
* Tries to deque a parameter set non blocking, and
|
||||
* sends it back to the requesting minion.
|
||||
* @param minion The minion asking for input
|
||||
*/
|
||||
void sendPendingExperimentData(Minion& minion);
|
||||
/**
|
||||
* Called after minion offers a result message.
|
||||
* Evaluates the Workload ID and puts the corresponding
|
||||
* job result into the result queue.
|
||||
* @param minion The minion offering results
|
||||
* @param workloadID The workload id of the result message
|
||||
*/
|
||||
void receiveExperimentResults(Minion& minion, uint32_t workloadID);
|
||||
public:
|
||||
CommThread(int sockfd, JobServer& p)
|
||||
: m_sock(sockfd), m_js(p) { }
|
||||
/**
|
||||
* The thread's entry point.
|
||||
*/
|
||||
void operator()();
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif //__JOB_SERVER_H__
|
||||
71
src/core/cpn/Minion.hpp
Normal file
71
src/core/cpn/Minion.hpp
Normal file
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* \brief The representation of a minion.
|
||||
*/
|
||||
|
||||
#ifndef __MINION_HPP__
|
||||
#define __MINION_HPP__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "comm/ExperimentData.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class Minion
|
||||
*
|
||||
* Contains all informations about a minion.
|
||||
*/
|
||||
class Minion {
|
||||
private:
|
||||
std::string hostname;
|
||||
bool isWorking;
|
||||
ExperimentData* currentExperimentData;
|
||||
int sockfd;
|
||||
public:
|
||||
Minion() : isWorking(false), currentExperimentData(0), sockfd(-1) { }
|
||||
/**
|
||||
* Sets the socket descriptor.
|
||||
* @param sock the new socket descriptor (used internal)
|
||||
*/
|
||||
void setSocketDescriptor(int sock) { sockfd = sock; }
|
||||
/**
|
||||
* Retrives the socket descriptor.
|
||||
* @return the socket descriptor
|
||||
*/
|
||||
int getSocketDescriptor() const { return (sockfd); }
|
||||
/**
|
||||
* Returns the hostname of the minion.
|
||||
* @return the hostname
|
||||
*/
|
||||
const std::string& getHostname() { return (hostname); }
|
||||
/**
|
||||
* Sets the hostname of the minion.
|
||||
* @param host the hostname
|
||||
*/
|
||||
void setHostname(const std::string& host) { hostname = host; }
|
||||
/**
|
||||
* Returns the current ExperimentData which the minion is working with.
|
||||
* @return a pointer of the current ExperimentData
|
||||
*/
|
||||
ExperimentData* getCurrentExperimentData() { return currentExperimentData; }
|
||||
/**
|
||||
* Sets the current ExperimentData which the minion is working with.
|
||||
* @param exp the current ExperimentData
|
||||
*/
|
||||
void setCurrentExperimentData(ExperimentData* exp) { currentExperimentData = exp; }
|
||||
/**
|
||||
* Returns the current state of the minion.
|
||||
* @return the current state
|
||||
*/
|
||||
bool isBusy() { return (isWorking); }
|
||||
/**
|
||||
* Sets the current state of the minion
|
||||
* @param state the current state
|
||||
*/
|
||||
void setBusy(bool state) { isWorking = state; }
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __MINION_HPP__
|
||||
10
src/core/efw/CMakeLists.txt
Normal file
10
src/core/efw/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
set(SRCS
|
||||
CoroutineManager.cc
|
||||
JobClient.cc
|
||||
)
|
||||
|
||||
# FIXME: Add dependency check for pcl-library here.
|
||||
|
||||
add_library(efw ${SRCS})
|
||||
|
||||
add_dependencies(efw comm)
|
||||
95
src/core/efw/CoroutineManager.cc
Normal file
95
src/core/efw/CoroutineManager.cc
Normal file
@ -0,0 +1,95 @@
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
#include "CoroutineManager.hpp"
|
||||
#include "ExperimentFlow.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
void CoroutineManager::m_invoke(void* pData)
|
||||
{
|
||||
//std::cerr << "CORO m_invoke " << co_current() << std::endl;
|
||||
// TODO: Log-Level?
|
||||
reinterpret_cast<ExperimentFlow*>(pData)->coroutine_entry();
|
||||
//m_togglerstack.pop();
|
||||
// FIXME: need to pop our caller
|
||||
co_exit(); // deletes the associated coroutine memory as well
|
||||
|
||||
// We really shouldn't get here:
|
||||
assert(false && "FATAL ERROR: CoroutineManager::m_invoke() -- shitstorm unloading!");
|
||||
while (1); // freeze.
|
||||
}
|
||||
|
||||
CoroutineManager::~CoroutineManager() { }
|
||||
|
||||
void CoroutineManager::toggle(ExperimentFlow* flow)
|
||||
{
|
||||
m_togglerstack.push(co_current());
|
||||
//std::cerr << "CORO toggle from " << m_togglerstack.top() << " to ";
|
||||
if (flow == SIM_FLOW) {
|
||||
co_call(m_simCoro);
|
||||
return;
|
||||
}
|
||||
|
||||
flowmap_t::iterator it = m_Flows.find(flow);
|
||||
assert(it != m_Flows.end() && "FATAL ERROR: Flow does not exist!");
|
||||
//std::cerr << it->second << std::endl;
|
||||
co_call(it->second);
|
||||
}
|
||||
|
||||
void CoroutineManager::create(ExperimentFlow* flow)
|
||||
{
|
||||
corohandle_t res = co_create(CoroutineManager::m_invoke, flow, NULL,
|
||||
STACK_SIZE_DEFAULT);
|
||||
//std::cerr << "CORO create " << res << std::endl;
|
||||
m_Flows.insert(std::pair<ExperimentFlow*,corohandle_t>(flow, res));
|
||||
}
|
||||
|
||||
void CoroutineManager::remove(ExperimentFlow* flow)
|
||||
{
|
||||
// find coroutine handle for this flow
|
||||
flowmap_t::iterator it = m_Flows.find(flow);
|
||||
if (it == m_Flows.end()) {
|
||||
assert(false && "FATAL ERROR: Cannot remove flow");
|
||||
return;
|
||||
}
|
||||
corohandle_t coro = it->second;
|
||||
//std::cerr << "CORO remove " << coro << std::endl;
|
||||
|
||||
// remove flow from active list
|
||||
m_Flows.erase(it);
|
||||
|
||||
// FIXME make sure resume() keeps working
|
||||
|
||||
// delete coroutine (and handle the special case we're removing
|
||||
// ourselves)
|
||||
if (coro == co_current()) {
|
||||
co_exit();
|
||||
} else {
|
||||
co_delete(coro);
|
||||
}
|
||||
}
|
||||
|
||||
void CoroutineManager::resume()
|
||||
{
|
||||
corohandle_t next = m_togglerstack.top();
|
||||
m_togglerstack.pop();
|
||||
//std::cerr << "CORO resume from " << co_current() << " to " << next << std::endl;
|
||||
co_call(next);
|
||||
}
|
||||
|
||||
ExperimentFlow* CoroutineManager::getCurrent()
|
||||
{
|
||||
coroutine_t cr = co_current();
|
||||
for (std::map<ExperimentFlow*,corohandle_t>::iterator it = m_Flows.begin();
|
||||
it != m_Flows.end(); it++)
|
||||
if (it->second == cr)
|
||||
return it->first;
|
||||
|
||||
assert(false && "FATAL ERROR: The current flow could not be retrieved!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const ExperimentFlow* CoroutineManager::SIM_FLOW = NULL;
|
||||
|
||||
} // end-of-namespace: fail
|
||||
72
src/core/efw/CoroutineManager.hpp
Normal file
72
src/core/efw/CoroutineManager.hpp
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef __COROUTINE_MANAGER_HPP__
|
||||
#define __COROUTINE_MANAGER_HPP__
|
||||
|
||||
#include <map>
|
||||
#include <stack>
|
||||
|
||||
#include <pcl.h> // the underlying "portable coroutine library"
|
||||
|
||||
namespace fail {
|
||||
|
||||
class ExperimentFlow;
|
||||
|
||||
/**
|
||||
* \class CoroutineManager
|
||||
* Manages the experiments flow encapsulated in coroutines. Each
|
||||
* experiment (flow) has it's associated data structure which is
|
||||
* represented by the ExperimentData-class.
|
||||
*/
|
||||
class CoroutineManager {
|
||||
private:
|
||||
//! the default stack size for coroutines (= experiment flows)
|
||||
static const unsigned STACK_SIZE_DEFAULT = 4096*4096;
|
||||
//! the abstraction for coroutine identification
|
||||
typedef coroutine_t corohandle_t;
|
||||
typedef std::map<ExperimentFlow*,corohandle_t> flowmap_t;
|
||||
//! the mapping "flows <-> coro-handle"
|
||||
flowmap_t m_Flows;
|
||||
//! the simulator/backend coroutine handle
|
||||
corohandle_t m_simCoro;
|
||||
//! stack of coroutines that explicitly activated another one with toggle()
|
||||
std::stack<corohandle_t> m_togglerstack;
|
||||
//! manages the run-calls for each ExperimentFlow-object
|
||||
static void m_invoke(void* pData);
|
||||
public:
|
||||
static const ExperimentFlow* SIM_FLOW; //!< the simulator coroutine flow
|
||||
|
||||
CoroutineManager() : m_simCoro(co_current()) { }
|
||||
~CoroutineManager();
|
||||
/**
|
||||
* Creates a new coroutine for the specified experiment flow.
|
||||
* @param flow the flow to be executed in the newly created coroutine
|
||||
*/
|
||||
void create(ExperimentFlow* flow);
|
||||
/**
|
||||
* Destroys coroutine for the specified experiment flow.
|
||||
* @param flow the flow to be removed
|
||||
*/
|
||||
void remove(ExperimentFlow* flow);
|
||||
/**
|
||||
* Switches the control flow to the experiment \a flow. If \a flow is
|
||||
* equal to \c SIM_FLOW, the control will be handed back to the
|
||||
* simulator. The current control flow is pushed onto an
|
||||
* internal stack.
|
||||
* @param flow the destination control flow or \c SIM_FLOW (= \c NULL )
|
||||
*/
|
||||
void toggle(ExperimentFlow* flow);
|
||||
/**
|
||||
* Gives the control back to the coroutine that toggle()d the
|
||||
* current one, by drawing from the internal stack built from
|
||||
* calls to toggle().
|
||||
*/
|
||||
void resume();
|
||||
/**
|
||||
* Retrieves the current (active) coroutine (= flow).
|
||||
* @return the current experiment flow.
|
||||
*/
|
||||
ExperimentFlow* getCurrent();
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __COROUTINE_MANAGER_HPP__
|
||||
37
src/core/efw/ExperimentFlow.hpp
Normal file
37
src/core/efw/ExperimentFlow.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef __EXPERIMENT_FLOW_HPP__
|
||||
#define __EXPERIMENT_FLOW_HPP__
|
||||
|
||||
#include "sal/SALInst.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class ExperimentFlow
|
||||
* Basic interface for user-defined experiments. To create a new experiment,
|
||||
* derive your own class from ExperimentFlow and define the run method.
|
||||
*/
|
||||
class ExperimentFlow {
|
||||
public:
|
||||
ExperimentFlow() { }
|
||||
/**
|
||||
* Defines the experiment flow.
|
||||
* @return \c true if the experiment was successful, \c false otherwise
|
||||
*/
|
||||
virtual bool run() = 0;
|
||||
/**
|
||||
* The entry point for this experiment's coroutine.
|
||||
* Should do some cleanup afterwards.
|
||||
*/
|
||||
void coroutine_entry()
|
||||
{
|
||||
run();
|
||||
simulator.clearEvents(this); // remove residual events
|
||||
// FIXME: Consider removing this call (see EventList.cc, void remove(ExperimentFlow* flow))
|
||||
// a) with the advantage that we will potentially prevent serious segfaults but
|
||||
// b) with the drawback that we cannot enforce any cleanups.
|
||||
}
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __EXPERIMENT_FLOW_HPP__
|
||||
133
src/core/efw/JobClient.cc
Normal file
133
src/core/efw/JobClient.cc
Normal file
@ -0,0 +1,133 @@
|
||||
#include "JobClient.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace fail {
|
||||
|
||||
JobClient::JobClient(const std::string& server, int port)
|
||||
{
|
||||
m_server_port = port;
|
||||
m_server = server;
|
||||
m_server_ent = gethostbyname(m_server.c_str());
|
||||
if(m_server_ent == NULL) {
|
||||
perror("[Client@gethostbyname()]");
|
||||
// TODO: Log-level?
|
||||
exit(1);
|
||||
}
|
||||
srand(time(NULL)); // needed for random backoff (see connectToServer)
|
||||
}
|
||||
|
||||
bool JobClient::connectToServer()
|
||||
{
|
||||
// Connect to server
|
||||
struct sockaddr_in serv_addr;
|
||||
m_sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if(m_sockfd < 0) {
|
||||
perror("[Client@socket()]");
|
||||
// TODO: Log-level?
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* Enable address reuse */
|
||||
int on = 1;
|
||||
setsockopt( m_sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) );
|
||||
|
||||
memset(&serv_addr, 0, sizeof(serv_addr));
|
||||
serv_addr.sin_family = AF_INET;
|
||||
memcpy(&serv_addr.sin_addr.s_addr, m_server_ent->h_addr, m_server_ent->h_length);
|
||||
serv_addr.sin_port = htons(m_server_port);
|
||||
|
||||
int retries = CLIENT_RETRY_COUNT;
|
||||
while (true) {
|
||||
if (connect(m_sockfd, (sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
|
||||
perror("[Client@connect()]");
|
||||
// TODO: Log-level?
|
||||
if (retries > 0) {
|
||||
// Wait CLIENT_RAND_BACKOFF_TSTART to RAND_BACKOFF_TEND seconds:
|
||||
int delay = rand() % (CLIENT_RAND_BACKOFF_TEND-CLIENT_RAND_BACKOFF_TSTART) + CLIENT_RAND_BACKOFF_TSTART;
|
||||
cout << "[Client] Retrying to connect to server in ~" << delay << "s..." << endl;
|
||||
// TODO: Log-level?
|
||||
sleep(delay);
|
||||
usleep(rand() % 1000000);
|
||||
--retries;
|
||||
continue;
|
||||
}
|
||||
cout << "[Client] Unable to reconnect (tried " << CLIENT_RETRY_COUNT << " times); "
|
||||
<< "I'll give it up!" << endl;
|
||||
// TODO: Log-level?
|
||||
return false; // finally: unable to connect, give it up :-(
|
||||
}
|
||||
break; // connected! :-)
|
||||
}
|
||||
cout << "[Client] Connection established!" << endl;
|
||||
// TODO: Log-level?
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JobClient::getParam(ExperimentData& exp)
|
||||
{
|
||||
while (1) { // Here we try to acquire a parameter set
|
||||
switch (tryToGetExperimentData(exp)) {
|
||||
// Jobserver will sent workload, params are set in \c exp
|
||||
case FailControlMessage_Command_WORK_FOLLOWS: return true;
|
||||
// Nothing to do right now, but maybe later
|
||||
case FailControlMessage_Command_COME_AGAIN:
|
||||
sleep(1);
|
||||
continue;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FailControlMessage_Command JobClient::tryToGetExperimentData(ExperimentData& exp)
|
||||
{
|
||||
// Connection failed, minion can die
|
||||
if (!connectToServer())
|
||||
return FailControlMessage_Command_DIE;
|
||||
|
||||
// Retrieve ExperimentData
|
||||
FailControlMessage ctrlmsg;
|
||||
ctrlmsg.set_command(FailControlMessage_Command_NEED_WORK);
|
||||
ctrlmsg.set_build_id(42);
|
||||
|
||||
SocketComm::sendMsg(m_sockfd, ctrlmsg);
|
||||
ctrlmsg.Clear();
|
||||
SocketComm::rcvMsg(m_sockfd, ctrlmsg);
|
||||
|
||||
switch (ctrlmsg.command()) {
|
||||
case FailControlMessage_Command_WORK_FOLLOWS:
|
||||
SocketComm::rcvMsg(m_sockfd, exp.getMessage());
|
||||
exp.setWorkloadID(ctrlmsg.workloadid()); // Store workload id of experiment data
|
||||
break;
|
||||
case FailControlMessage_Command_COME_AGAIN:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
close(m_sockfd);
|
||||
return ctrlmsg.command();
|
||||
}
|
||||
|
||||
bool JobClient::sendResult(ExperimentData& result)
|
||||
{
|
||||
if (!connectToServer())
|
||||
return false;
|
||||
|
||||
// Send back results
|
||||
FailControlMessage ctrlmsg;
|
||||
ctrlmsg.set_command(FailControlMessage_Command_RESULT_FOLLOWS);
|
||||
ctrlmsg.set_build_id(42);
|
||||
ctrlmsg.set_workloadid(result.getWorkloadID());
|
||||
cout << "[Client] Sending back result [" << std::dec << result.getWorkloadID() << "]..." << endl;
|
||||
// TODO: Log-level?
|
||||
SocketComm::sendMsg(m_sockfd, ctrlmsg);
|
||||
SocketComm::sendMsg(m_sockfd, result.getMessage());
|
||||
|
||||
// Close connection.
|
||||
close(m_sockfd);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // end-of-namespace: fail
|
||||
57
src/core/efw/JobClient.hpp
Normal file
57
src/core/efw/JobClient.hpp
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef __JOB_CLIENT_H__
|
||||
#define __JOB_CLIENT_H__
|
||||
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "comm/SocketComm.hpp"
|
||||
#include "comm/ExperimentData.hpp"
|
||||
#include "comm/msg/FailControlMessage.pb.h"
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class JobClient
|
||||
*
|
||||
* \brief Manages communication with JobServer
|
||||
* The Minion's JobClient requests ExperimentData and returns results.
|
||||
*/
|
||||
class JobClient {
|
||||
private:
|
||||
std::string m_server;
|
||||
int m_server_port;
|
||||
struct hostent* m_server_ent;
|
||||
int m_sockfd;
|
||||
|
||||
bool connectToServer();
|
||||
|
||||
FailControlMessage_Command tryToGetExperimentData(ExperimentData& exp);
|
||||
public:
|
||||
JobClient(const std::string& server = "localhost", int port = 1111);
|
||||
/**
|
||||
* Receive experiment data set from (remote) JobServer
|
||||
* The caller (experiment developer) is responsible for
|
||||
* allocating his ExperimentData object.
|
||||
*
|
||||
* @param exp Reference to a ExperimentData object allocated by the caller!
|
||||
* @return \c true if parameter have been received and put into \c exp, \c false else.
|
||||
*/
|
||||
bool getParam(ExperimentData& exp);
|
||||
/**
|
||||
* Send back experiment result to the (remote) JobServer
|
||||
* The caller (experiment developer) is responsible for
|
||||
* destroying his ExperimentData object afterwards.
|
||||
*
|
||||
* @param result Reference to the ExperimentData holding result values
|
||||
* @return \c true Result successfully sent, \c false else.
|
||||
*/
|
||||
bool sendResult(ExperimentData& result);
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __JOB_CLIENT_H__
|
||||
83
src/core/sal/BufferCache.cc
Normal file
83
src/core/sal/BufferCache.cc
Normal file
@ -0,0 +1,83 @@
|
||||
#include "BufferCache.hpp"
|
||||
#include "Event.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
template<class T>
|
||||
int BufferCache<T>::add(T val)
|
||||
{
|
||||
size_t new_size = getCount() + 1;
|
||||
size_t new_last_index = getCount();
|
||||
|
||||
int res = reallocate_buffer(new_size);
|
||||
if (res == 0) {
|
||||
set(new_last_index, val);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
int BufferCache<T>::remove(T val)
|
||||
{
|
||||
bool do_remove = false;
|
||||
for (size_t i = 0; i < getCount(); i++) {
|
||||
if (get(i) == val) {
|
||||
do_remove = true;
|
||||
}
|
||||
if (do_remove) {
|
||||
if (i > getCount() - 1) {
|
||||
set(i, get(i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int res = 0;
|
||||
if (do_remove) {
|
||||
size_t new_size = getCount() - 1;
|
||||
res = reallocate_buffer(new_size);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void BufferCache<T>::clear()
|
||||
{
|
||||
setCount(0);
|
||||
free(m_Buffer);
|
||||
m_Buffer = NULL;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
int BufferCache<T>::erase(int idx)
|
||||
{
|
||||
for (size_t i = idx; i < getCount() - 1; i++) {
|
||||
set(i, get(i + 1));
|
||||
}
|
||||
|
||||
size_t new_size = getCount() - 1;
|
||||
if (reallocate_buffer(new_size) != 0)
|
||||
return -1;
|
||||
return idx;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
int BufferCache<T>::reallocate_buffer(size_t new_size)
|
||||
{
|
||||
if (new_size == 0) {
|
||||
clear();
|
||||
return 0;
|
||||
}
|
||||
void *new_buffer = realloc(m_Buffer, new_size * sizeof(T));
|
||||
if (new_buffer == NULL)
|
||||
return 10;
|
||||
m_Buffer = static_cast<T*>(new_buffer);
|
||||
setCount(new_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Declare whatever instances of the template you are going to use here:
|
||||
template class BufferCache<BPEvent*>;
|
||||
|
||||
} // end-of-namespace: fail
|
||||
93
src/core/sal/BufferCache.hpp
Normal file
93
src/core/sal/BufferCache.hpp
Normal file
@ -0,0 +1,93 @@
|
||||
#ifndef __BUFFER_CACHE_HPP__
|
||||
#define __BUFFER_CACHE_HPP__
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
// FIXME: (Maybe) This should be located in utils, because
|
||||
// it's "Fail*-independend"...?
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class BufferCache
|
||||
*
|
||||
* \brief A simple dynamic array
|
||||
*
|
||||
* This class is intended to serve as a kind of cache for the entirely STL-based,
|
||||
* untyped and therefore quite slow event handling mechanism of Fail*.
|
||||
* To keep the code easily readable, the buffer management methods
|
||||
* are less performant than the could be (remove() and erase() have linear complexity).
|
||||
*
|
||||
* FIXME: This desription sounds like a contradiction...
|
||||
* (-> "quite slow event handling" vs. "are less performant than the could be")
|
||||
*
|
||||
* FIXME: Why not using std::vector? ("A simple dynamic array")
|
||||
*/
|
||||
template<class T>
|
||||
class BufferCache {
|
||||
private:
|
||||
// TODO: comments ("//!<") needed!
|
||||
T *m_Buffer;
|
||||
size_t m_BufferCount;
|
||||
protected:
|
||||
/**
|
||||
* Changes the current length of the array. Should be inlined.
|
||||
* @param new_count the new array length
|
||||
*/
|
||||
inline void setCount(size_t new_count) { m_BufferCount = new_count; }
|
||||
/**
|
||||
* Reallocates the buffer. This implementation is extremely primitive,
|
||||
* but since the amount of entries is small,
|
||||
* this will not be significant, hopefully. Should be inlined.
|
||||
* @param new_size the new number of elements in the array
|
||||
* @return 0 if successful, an error code otherwise (ATM only 10 if malloc() fails)
|
||||
*/
|
||||
inline int reallocate_buffer(size_t new_size);
|
||||
public:
|
||||
BufferCache()
|
||||
: m_Buffer(NULL), m_BufferCount(0) {}
|
||||
~BufferCache() {}
|
||||
/**
|
||||
* Add an element to the array. The object pointed to remains untouched.
|
||||
* @param val the element to add
|
||||
* @return 0 if successful, an error code otherwise (ATM only 10 if malloc() fails)
|
||||
*/
|
||||
int add(T val);
|
||||
/**
|
||||
* Remove an element from the array. The object pointed to remains untouched.
|
||||
* @param val the element to remove
|
||||
* @return 0 if successful, an error code otherwise (ATM only 10 if malloc() fails)
|
||||
*/
|
||||
int remove(T val);
|
||||
/**
|
||||
* Remove an element at a specific position. The object pointed to remains untouched.
|
||||
* @param val the element to remove
|
||||
* @return a pointer to the given element's successor if successful, -1 otherwise
|
||||
*/
|
||||
int erase(int i);
|
||||
/**
|
||||
* Clears the array, removing all elements. The objects pointed to remain untouched.
|
||||
*/
|
||||
void clear();
|
||||
/**
|
||||
* Retrieve an element from the array. Should be inlined.
|
||||
* @param idx the position to retrieve the element from
|
||||
* @return the element at the given position
|
||||
*/
|
||||
inline T get(size_t idx) { return m_Buffer[idx]; }
|
||||
/**
|
||||
* Set an element at a given position. Should be inlined.
|
||||
* @param idx the position to change an element at
|
||||
* @param val the new value of the given element
|
||||
*/
|
||||
inline void set(size_t idx, T val) { m_Buffer[idx] = val; }
|
||||
/**
|
||||
* Retrieves the current length of the array. Should be inlined.
|
||||
* @return the array length
|
||||
*/
|
||||
inline size_t getCount() { return m_BufferCount; }
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __BUFFER_CACHE_HPP__
|
||||
25
src/core/sal/CMakeLists.txt
Normal file
25
src/core/sal/CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
||||
if(BUILD_BOCHS)
|
||||
set(SRCS
|
||||
BufferCache.cc
|
||||
Event.cc
|
||||
EventList.cc
|
||||
Memory.cc
|
||||
Register.cc
|
||||
SimulatorController.cc
|
||||
bochs/BochsController.cc
|
||||
)
|
||||
else()
|
||||
set(SRCS
|
||||
BufferCache.cc
|
||||
Event.cc
|
||||
EventList.cc
|
||||
Memory.cc
|
||||
Register.cc
|
||||
SimulatorController.cc
|
||||
${VARIANT}/OVPController.cc
|
||||
)
|
||||
endif(BUILD_BOCHS)
|
||||
|
||||
add_library(sal ${SRCS})
|
||||
|
||||
add_dependencies(sal efw)
|
||||
87
src/core/sal/Event.cc
Normal file
87
src/core/sal/Event.cc
Normal file
@ -0,0 +1,87 @@
|
||||
#include "Event.hpp"
|
||||
#include "SALInst.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
EventId BaseEvent::m_Counter = 0;
|
||||
|
||||
bool TroubleEvent::isMatching(unsigned troubleNum) const
|
||||
{
|
||||
for(unsigned i = 0; i < m_WatchNumbers.size(); i++)
|
||||
{
|
||||
if(m_WatchNumbers[i] == troubleNum ||
|
||||
m_WatchNumbers[i] == ANY_TRAP)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TroubleEvent::removeWatchNumber(unsigned troubleNum)
|
||||
{
|
||||
for(unsigned i = 0; i < m_WatchNumbers.size(); i++)
|
||||
{
|
||||
if(m_WatchNumbers[i] == troubleNum)
|
||||
{
|
||||
m_WatchNumbers.erase(m_WatchNumbers.begin()+i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TroubleEvent::addWatchNumber(unsigned troubleNumber)
|
||||
{
|
||||
for(unsigned i = 0; i < m_WatchNumbers.size(); i++)
|
||||
{
|
||||
|
||||
if(m_WatchNumbers[i] == troubleNumber)
|
||||
return false;
|
||||
}
|
||||
m_WatchNumbers.push_back(troubleNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MemAccessEvent::isMatching(address_t addr, accessType_t accesstype) const
|
||||
{
|
||||
if(!(m_WatchType & accesstype))
|
||||
return (false);
|
||||
else if(m_WatchAddr != addr &&
|
||||
m_WatchAddr != ANY_ADDR)
|
||||
return (false);
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool BPEvent::aspaceIsMatching(address_t aspace) const
|
||||
{
|
||||
if (m_CR3 == ANY_ADDR || m_CR3 == aspace)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void BPRangeEvent::setWatchInstructionPointerRange(address_t start, address_t end)
|
||||
{
|
||||
m_WatchStartAddr = start;
|
||||
m_WatchEndAddr = end;
|
||||
}
|
||||
|
||||
bool BPRangeEvent::isMatching(address_t addr, address_t aspace) const
|
||||
{
|
||||
if (!aspaceIsMatching(aspace))
|
||||
return false;
|
||||
if ((m_WatchStartAddr != ANY_ADDR && addr < m_WatchStartAddr) ||
|
||||
(m_WatchEndAddr != ANY_ADDR && addr > m_WatchEndAddr))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BPSingleEvent::isMatching(address_t addr, address_t aspace) const
|
||||
{
|
||||
if (aspaceIsMatching(aspace)) {
|
||||
if (m_WatchInstrPtr == ANY_ADDR || m_WatchInstrPtr == addr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // end-of-namespace: fail
|
||||
622
src/core/sal/Event.hpp
Normal file
622
src/core/sal/Event.hpp
Normal file
@ -0,0 +1,622 @@
|
||||
#ifndef __EVENT_HPP__
|
||||
#define __EVENT_HPP__
|
||||
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
|
||||
#include "SALConfig.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
class ExperimentFlow;
|
||||
|
||||
typedef unsigned long EventId; //!< type of event ids
|
||||
|
||||
//! invalid event id (used as a return indicator)
|
||||
const EventId INVALID_EVENT = (EventId)-1;
|
||||
//! address wildcard (e.g. for BPEvent's)
|
||||
const address_t ANY_ADDR = static_cast<address_t>(-1);
|
||||
//! instruction wildcard
|
||||
const unsigned ANY_INSTR = static_cast<unsigned>(-1);
|
||||
//! trap wildcard
|
||||
const unsigned ANY_TRAP = static_cast<unsigned>(-1);
|
||||
//! interrupt wildcard
|
||||
const unsigned ANY_INTERRUPT = static_cast<unsigned>(-1);
|
||||
|
||||
/**
|
||||
* \class BaseEvent
|
||||
* This is the base class for all event types.
|
||||
*/
|
||||
class BaseEvent
|
||||
{
|
||||
private:
|
||||
//! current class-scoped id counter to provide \a unique id's
|
||||
static EventId m_Counter;
|
||||
protected:
|
||||
EventId m_Id; //!< unique id of this event
|
||||
time_t m_tStamp; //!< time stamp of event
|
||||
unsigned int m_OccCounter; //!< event fires when 0 is reached
|
||||
unsigned int m_OccCounterInit; //!< initial value for m_OccCounter
|
||||
ExperimentFlow* m_Parent; //!< this event belongs to experiment m_Parent
|
||||
public:
|
||||
BaseEvent() : m_Id(++m_Counter), m_OccCounter(1), m_OccCounterInit(1), m_Parent(NULL)
|
||||
{ updateTime(); }
|
||||
virtual ~BaseEvent() { }
|
||||
/**
|
||||
* Retrieves the unique event id for this event.
|
||||
* @return the unique id
|
||||
*/
|
||||
EventId getId() const { return (m_Id); }
|
||||
/**
|
||||
* Retrieves the time stamp of this event. The time stamp is set when
|
||||
* the event gets created, id est the constructor is called. The meaning
|
||||
* of this value depends on the actual event type.
|
||||
* @return the time stamp
|
||||
*/
|
||||
std::time_t getTime() const { return (m_tStamp); }
|
||||
/**
|
||||
* Decreases the event counter by one. When this counter reaches zero, the
|
||||
* event will be triggered.
|
||||
*/
|
||||
void decreaseCounter() { --m_OccCounter; }
|
||||
/**
|
||||
* Sets the event counter to the specified value (default is one).
|
||||
* @param val the new counter value
|
||||
*/
|
||||
void setCounter(unsigned int val = 1) { m_OccCounter = m_OccCounterInit = val; }
|
||||
/**
|
||||
* Returns the current counter value.
|
||||
* @return the counter value
|
||||
*/
|
||||
unsigned int getCounter() const { return (m_OccCounter); }
|
||||
/**
|
||||
* Resets the event counter to its default value, or the last
|
||||
* value that was set through setCounter().
|
||||
*/
|
||||
void resetCounter() { m_OccCounter = m_OccCounterInit; }
|
||||
/**
|
||||
* Sets the time stamp for this event to the current system time.
|
||||
*/
|
||||
void updateTime() { time(&m_tStamp); }
|
||||
/**
|
||||
* Returns the parent experiment of this event (context).
|
||||
* If the event was created temporarily or wasn't linked to a context,
|
||||
* the return value is \c NULL (unknown identity).
|
||||
*/
|
||||
ExperimentFlow* getParent() const { return (m_Parent); }
|
||||
/**
|
||||
* Sets the parent (context) of this event. The default context
|
||||
* is \c NULL (unknown identity).
|
||||
*/
|
||||
void setParent(ExperimentFlow* pFlow) { m_Parent = pFlow; }
|
||||
};
|
||||
// ----------------------------------------------------------------------------
|
||||
// Specialized events:
|
||||
//
|
||||
|
||||
/**
|
||||
* \class BEvent
|
||||
* A Breakpoint event to observe instruction changes within a given address space.
|
||||
*/
|
||||
class BPEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
address_t m_CR3;
|
||||
address_t m_TriggerInstrPtr;
|
||||
public:
|
||||
/**
|
||||
* Creates a new breakpoint event. The range information is specific to
|
||||
* the subclasses.
|
||||
* @param address_space the address space to be oberserved, given as the
|
||||
* content of a CR3 register. The event will not be triggered unless
|
||||
* \a ip is part of the given address space.
|
||||
* ANY_ADDR can be used as a placeholder to allow debugging
|
||||
* in a random address space.
|
||||
*/
|
||||
BPEvent(address_t address_space = ANY_ADDR)
|
||||
: m_CR3(address_space), m_TriggerInstrPtr(ANY_ADDR)
|
||||
{}
|
||||
/**
|
||||
* Returns the address space register of this event.
|
||||
*/
|
||||
address_t getCR3() const
|
||||
{ return m_CR3; }
|
||||
/**
|
||||
* Sets the address space register for this event.
|
||||
*/
|
||||
void setCR3(address_t iptr)
|
||||
{ m_CR3 = iptr; }
|
||||
/**
|
||||
* Checks whether a given address space is matching.
|
||||
*/
|
||||
bool aspaceIsMatching(address_t address_space = ANY_ADDR) const;
|
||||
/**
|
||||
* Checks whether a given address is matching.
|
||||
*/
|
||||
virtual bool isMatching(address_t addr = 0, address_t address_space = ANY_ADDR) const = 0;
|
||||
/**
|
||||
* Returns the instruction pointer that triggered this event.
|
||||
*/
|
||||
address_t getTriggerInstructionPointer() const
|
||||
{ return m_TriggerInstrPtr; }
|
||||
/**
|
||||
* Sets the instruction pointer that triggered this event. Should not
|
||||
* be used by experiment code.
|
||||
*/
|
||||
void setTriggerInstructionPointer(address_t iptr)
|
||||
{ m_TriggerInstrPtr = iptr; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BPSingleEvent
|
||||
* A Breakpoint event to observe specific instruction pointers.
|
||||
*/
|
||||
class BPSingleEvent : virtual public BPEvent
|
||||
{
|
||||
private:
|
||||
address_t m_WatchInstrPtr;
|
||||
public:
|
||||
/**
|
||||
* Creates a new breakpoint event.
|
||||
* @param ip the instruction pointer of the breakpoint. If the control
|
||||
* flow reaches this address and its counter value is zero, the
|
||||
* event will be triggered. \a eip can be set to the ANY_ADDR
|
||||
* wildcard to allow arbitrary addresses.
|
||||
* @param address_space the address space to be oberserved, given as the
|
||||
* content of a CR3 register. The event will not be triggered unless
|
||||
* \a ip is part of the given address space.
|
||||
* Here, too, ANY_ADDR is a placeholder to allow debugging
|
||||
* in a random address space.
|
||||
*/
|
||||
BPSingleEvent(address_t ip = 0, address_t address_space = ANY_ADDR)
|
||||
: BPEvent(address_space), m_WatchInstrPtr(ip) { }
|
||||
/**
|
||||
* Returns the instruction pointer this event waits for.
|
||||
*/
|
||||
address_t getWatchInstructionPointer() const
|
||||
{ return m_WatchInstrPtr; }
|
||||
/**
|
||||
* Sets the instruction pointer this event waits for.
|
||||
*/
|
||||
void setWatchInstructionPointer(address_t iptr)
|
||||
{ m_WatchInstrPtr = iptr; }
|
||||
/**
|
||||
* Checks whether a given address is matching.
|
||||
*/
|
||||
bool isMatching(address_t addr, address_t address_space) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BPRangeEvent
|
||||
* A event type to observe ranges of instruction pointers.
|
||||
*/
|
||||
class BPRangeEvent : virtual public BPEvent
|
||||
{
|
||||
private:
|
||||
address_t m_WatchStartAddr;
|
||||
address_t m_WatchEndAddr;
|
||||
public:
|
||||
/**
|
||||
* Creates a new breakpoint-range event. The range's ends are both
|
||||
* inclusive, i.e. an address matches if start <= addr <= end.
|
||||
* ANY_ADDR denotes the lower respectively the upper end of the address
|
||||
* space.
|
||||
*/
|
||||
BPRangeEvent(address_t start = 0, address_t end = 0, address_t address_space = ANY_ADDR)
|
||||
: BPEvent(address_space), m_WatchStartAddr(start), m_WatchEndAddr(end)
|
||||
{ }
|
||||
/**
|
||||
* Returns the instruction pointer watch range of this event.
|
||||
*/
|
||||
std::pair<address_t, address_t> getWatchInstructionPointerRange() const
|
||||
{ return std::make_pair(m_WatchStartAddr, m_WatchEndAddr); }
|
||||
/**
|
||||
* Sets the instruction pointer watch range. Both ends of the range
|
||||
* may be ANY_ADDR (cf. constructor).
|
||||
*/
|
||||
void setWatchInstructionPointerRange(address_t start,
|
||||
address_t end);
|
||||
/**
|
||||
* Checks whether a given address is within the range.
|
||||
*/
|
||||
bool isMatching(address_t addr, address_t address_space) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* \class MemAccessEvent
|
||||
* Observes memory read/write accesses.
|
||||
* FIXME? currently >8-bit accesses only match if their lowest address is being watched
|
||||
* (e.g., a 32-bit write to 0x4 also accesses 0x7, but this cannot be matched)
|
||||
*/
|
||||
class MemAccessEvent : virtual public BaseEvent
|
||||
{
|
||||
public:
|
||||
enum accessType_t
|
||||
{
|
||||
MEM_UNKNOWN = 0x0,
|
||||
MEM_READ = 0x1,
|
||||
MEM_WRITE = 0x2,
|
||||
MEM_READWRITE = 0x3
|
||||
};
|
||||
private:
|
||||
//! Specific guest system address to watch, or ANY_ADDR.
|
||||
address_t m_WatchAddr;
|
||||
/**
|
||||
* Memory access type we want to watch
|
||||
* (MEM_READ || MEM_WRITE || MEM_READWRITE).
|
||||
*/
|
||||
accessType_t m_WatchType;
|
||||
//! Specific guest system address that actually triggered the event.
|
||||
address_t m_TriggerAddr;
|
||||
//! Width of the memory access (# bytes).
|
||||
size_t m_TriggerWidth;
|
||||
//! Address of the instruction that caused the memory access.
|
||||
address_t m_TriggerIP;
|
||||
//! Memory access type at m_TriggerAddr.
|
||||
accessType_t m_AccessType;
|
||||
public:
|
||||
MemAccessEvent(accessType_t watchtype = MEM_READWRITE)
|
||||
: m_WatchAddr(ANY_ADDR), m_WatchType(watchtype),
|
||||
m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR),
|
||||
m_AccessType(MEM_UNKNOWN) { }
|
||||
MemAccessEvent(address_t addr,
|
||||
accessType_t watchtype = MEM_READWRITE)
|
||||
: m_WatchAddr(addr), m_WatchType(watchtype),
|
||||
m_TriggerAddr(ANY_ADDR), m_TriggerIP(ANY_ADDR),
|
||||
m_AccessType(MEM_UNKNOWN) { }
|
||||
/**
|
||||
* Returns the memory address to be observed.
|
||||
*/
|
||||
address_t getWatchAddress() const { return m_WatchAddr; }
|
||||
/**
|
||||
* Sets the memory address to be observed. (Wildcard: ANY_ADDR)
|
||||
*/
|
||||
void setWatchAddress(address_t addr) { m_WatchAddr = addr; }
|
||||
/**
|
||||
* Checks whether a given address is matching.
|
||||
*/
|
||||
bool isMatching(address_t addr, accessType_t accesstype) const;
|
||||
/**
|
||||
* Returns the specific memory address that actually triggered the
|
||||
* event.
|
||||
*/
|
||||
address_t getTriggerAddress() const { return (m_TriggerAddr); }
|
||||
/**
|
||||
* Sets the specific memory address that actually triggered the event.
|
||||
* Should not be used by experiment code.
|
||||
*/
|
||||
void setTriggerAddress(address_t addr) { m_TriggerAddr = addr; }
|
||||
/**
|
||||
* Returns the specific memory address that actually triggered the
|
||||
* event.
|
||||
*/
|
||||
size_t getTriggerWidth() const { return (m_TriggerWidth); }
|
||||
/**
|
||||
* Sets the specific memory address that actually triggered the event.
|
||||
* Should not be used by experiment code.
|
||||
*/
|
||||
void setTriggerWidth(size_t width) { m_TriggerWidth = width; }
|
||||
/**
|
||||
* Returns the address of the instruction causing this memory
|
||||
* access.
|
||||
*/
|
||||
address_t getTriggerInstructionPointer() const
|
||||
{ return (m_TriggerIP); }
|
||||
/**
|
||||
* Sets the address of the instruction causing this memory
|
||||
* access. Should not be used by experiment code.
|
||||
*/
|
||||
void setTriggerInstructionPointer(address_t addr)
|
||||
{ m_TriggerIP = addr; }
|
||||
/**
|
||||
* Returns type (MEM_READ || MEM_WRITE) of the memory access that
|
||||
* triggered this event.
|
||||
*/
|
||||
accessType_t getTriggerAccessType() const { return (m_AccessType); }
|
||||
/**
|
||||
* Sets type of the memory access that triggered this event. Should
|
||||
* not be used by experiment code.
|
||||
*/
|
||||
void setTriggerAccessType(accessType_t type) { m_AccessType = type; }
|
||||
/**
|
||||
* Returns memory access types (MEM_READ || MEM_WRITE || MEM_READWRITE)
|
||||
* this event watches. Should not be used by experiment code.
|
||||
*/
|
||||
accessType_t getWatchAccessType() const { return (m_WatchType); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class MemReadEvent
|
||||
* Observes memory read accesses.
|
||||
*/
|
||||
class MemReadEvent : virtual public MemAccessEvent
|
||||
{
|
||||
public:
|
||||
MemReadEvent()
|
||||
: MemAccessEvent(MEM_READ) { }
|
||||
MemReadEvent(address_t addr)
|
||||
: MemAccessEvent(addr, MEM_READ) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class MemWriteEvent
|
||||
* Observes memory write accesses.
|
||||
*/
|
||||
class MemWriteEvent : virtual public MemAccessEvent
|
||||
{
|
||||
public:
|
||||
MemWriteEvent()
|
||||
: MemAccessEvent(MEM_READ) { }
|
||||
MemWriteEvent(address_t addr)
|
||||
: MemAccessEvent(addr, MEM_WRITE) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class TroubleEvent
|
||||
* Observes interrupt/trap activties.
|
||||
*/
|
||||
class TroubleEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* Specific guest system interrupt/trap number that actually
|
||||
* trigger the event.
|
||||
*/
|
||||
int m_TriggerNumber;
|
||||
/**
|
||||
* Specific guest system interrupt/trap numbers to watch,
|
||||
* or ANY_INTERRUPT/ANY_TRAP.
|
||||
*/
|
||||
std::vector<unsigned> m_WatchNumbers;
|
||||
public:
|
||||
TroubleEvent() : m_TriggerNumber (-1) { }
|
||||
TroubleEvent(unsigned troubleNumber)
|
||||
: m_TriggerNumber(-1)
|
||||
{ addWatchNumber(troubleNumber); }
|
||||
/**
|
||||
* Add an interrupt/trap-number which should be observed
|
||||
* @param troubleNumber number of an interrupt or trap
|
||||
* @return \c true if number is added to the list \c false if the number
|
||||
* was already in the list
|
||||
*/
|
||||
bool addWatchNumber(unsigned troubleNumber);
|
||||
/**
|
||||
* Remove an interrupt/trap-number which is in the list of observed
|
||||
* numbers
|
||||
* @param troubleNumber number of an interrupt or trap
|
||||
* @return \c true if the number was found and removed \c false if the
|
||||
* number was not in the list
|
||||
*/
|
||||
bool removeWatchNumber(unsigned troubleNum);
|
||||
/**
|
||||
* Returns the list of observed numbers
|
||||
* @return a copy of the list which contains all observed numbers
|
||||
*/
|
||||
std::vector<unsigned> getWatchNumbers() { return (m_WatchNumbers); }
|
||||
/**
|
||||
* Checks whether a given interrupt-/trap-number is matching.
|
||||
*/
|
||||
bool isMatching(unsigned troubleNum) const;
|
||||
/**
|
||||
* Sets the specific interrupt-/trap-number that actually triggered
|
||||
* the event. Should not be used by experiment code.
|
||||
*/
|
||||
void setTriggerNumber(unsigned troubleNum)
|
||||
{ m_TriggerNumber = troubleNum; }
|
||||
/**
|
||||
* Returns the specific interrupt-/trap-number that actually triggered
|
||||
* the event.
|
||||
*/
|
||||
unsigned getTriggerNumber() { return (m_TriggerNumber); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class InterruptEvent
|
||||
* Observes interrupts of the guest system.
|
||||
*/
|
||||
class InterruptEvent : virtual public TroubleEvent
|
||||
{
|
||||
private:
|
||||
bool m_IsNMI; //!< non maskable interrupt flag
|
||||
public:
|
||||
InterruptEvent() : m_IsNMI(false) { }
|
||||
InterruptEvent(unsigned interrupt) : m_IsNMI(false)
|
||||
{ addWatchNumber(interrupt); }
|
||||
/**
|
||||
* Returns \c true if the interrupt is non maskable, \c false otherwise.
|
||||
*/
|
||||
bool isNMI() { return (m_IsNMI); }
|
||||
/**
|
||||
* Sets the interrupt type (non maskable or not).
|
||||
*/
|
||||
void setNMI(bool enabled) { m_IsNMI = enabled; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class TrapEvent
|
||||
* Observes traps of the guest system.
|
||||
*/
|
||||
class TrapEvent : virtual public TroubleEvent
|
||||
{
|
||||
public:
|
||||
TrapEvent() { }
|
||||
TrapEvent(unsigned trap) { addWatchNumber(trap); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class GuestEvent
|
||||
* Used to receive data from the guest system.
|
||||
*/
|
||||
class GuestEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
char m_Data;
|
||||
unsigned m_Port;
|
||||
public:
|
||||
GuestEvent() : m_Data(0), m_Port(0) { }
|
||||
/**
|
||||
* Returns the data, transmitted by the guest system.
|
||||
*/
|
||||
char getData() const { return (m_Data); }
|
||||
/**
|
||||
* Sets the data which had been transmitted by the guest system.
|
||||
*/
|
||||
void setData(char data) { m_Data = data; }
|
||||
/**
|
||||
* Returns the data length, transmitted by the guest system.
|
||||
*/
|
||||
unsigned getPort() const { return (m_Port); }
|
||||
/**
|
||||
* Sets the data length which had been transmitted by the guest system.
|
||||
*/
|
||||
void setPort(unsigned port) { m_Port = port; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class IOPortEvent
|
||||
* Observes I/O access on architectures with a separate I/O access mechanism (e.g. IA-32)
|
||||
*/
|
||||
class IOPortEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
unsigned char m_Data;
|
||||
unsigned m_Port;
|
||||
bool m_Out;
|
||||
public:
|
||||
/**
|
||||
* Initialises an IOPortEvent
|
||||
*
|
||||
* @param port the port the event ist listening on
|
||||
* @param out Defines the direction of the event.
|
||||
* \arg \c true Output on the given port is captured.
|
||||
* \arg \c false Input on the given port is captured.
|
||||
*/
|
||||
IOPortEvent(unsigned port, bool out) : m_Data(0), m_Port(port), m_Out(out) { }
|
||||
/**
|
||||
* Returns the data sent to the specified port
|
||||
*/
|
||||
unsigned char getData() const { return (m_Data); }
|
||||
/**
|
||||
* Sets the data which had been transmitted.
|
||||
*/
|
||||
void setData(unsigned char data) { m_Data = data; }
|
||||
/**
|
||||
* Retrieves the port which this event is bound to.
|
||||
*/
|
||||
unsigned getPort() const { return (m_Port); }
|
||||
/**
|
||||
* Sets the port which this event is bound to.
|
||||
*/
|
||||
void setPort(unsigned port) { m_Port = port; }
|
||||
/**
|
||||
* Checks whether a given port number is matching.
|
||||
* @param p The port number an I/O event occured on
|
||||
* @param out True if the communication was outbound, false otherwise
|
||||
*/
|
||||
bool isMatching(unsigned p, bool out) const { return ( out = isOutEvent() && p == getPort()); }
|
||||
/**
|
||||
* Tells you if this event is capturing outbound communication (inbound if false)
|
||||
*/
|
||||
bool isOutEvent() const { return m_Out; }
|
||||
/**
|
||||
* Change the event direction.
|
||||
* \arg \c true Output on the given port is captured.
|
||||
* \arg \c false Input on the given port is captured.
|
||||
*/
|
||||
void setOut(bool out) { m_Out = out; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class JumpEvent
|
||||
* JumpEvents are used to observe conditional jumps (if...else if...else).
|
||||
*/
|
||||
class JumpEvent : virtual public BaseEvent
|
||||
{
|
||||
private:
|
||||
unsigned m_Opcode;
|
||||
bool m_FlagTriggered;
|
||||
public:
|
||||
/**
|
||||
* Constructs a new event object.
|
||||
* @param parent the parent object
|
||||
* @param opcode the opcode of the jump-instruction to be observed
|
||||
* or ANY_INSTR to match all jump-instructions
|
||||
*/
|
||||
JumpEvent(unsigned opcode = ANY_INSTR)
|
||||
: m_Opcode(opcode), m_FlagTriggered(false) { }
|
||||
/**
|
||||
* Retrieves the opcode of the jump-instruction.
|
||||
*/
|
||||
unsigned getOpcode() const { return (m_Opcode); }
|
||||
/**
|
||||
* Returns \c true, of the event was triggered due to specific register
|
||||
* content, \c false otherwise.
|
||||
*/
|
||||
bool isRegisterTriggered() { return (!m_FlagTriggered); }
|
||||
/**
|
||||
* Returns \c true, of the event was triggered due to specific FLAG
|
||||
* state, \c false otherwise. This is the common case.
|
||||
*/
|
||||
bool isFlagTriggered() { return (m_FlagTriggered); }
|
||||
/**
|
||||
* Sets the requestet jump-instruction opcode.
|
||||
*/
|
||||
void setOpcode(unsigned oc) { oc = m_Opcode; }
|
||||
/**
|
||||
* Sets the trigger flag.
|
||||
*/
|
||||
void setFlagTriggered(bool flagTriggered)
|
||||
{ m_FlagTriggered = flagTriggered; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class TimerEvent
|
||||
* This event type is used to create timeouts/timers within in an experiment.
|
||||
*/
|
||||
class TimerEvent : public BaseEvent
|
||||
{
|
||||
private:
|
||||
unsigned m_Timeout; //!< timeout interval in milliseconds
|
||||
timer_id_t m_Id; //!< internal timer id (sim-specific)
|
||||
bool m_Once; //!< true, if the timer should be triggered only once
|
||||
public:
|
||||
/**
|
||||
* Creates a new timer event. This can be used to implement a timeout-
|
||||
* mechanism in the experiment-flow. The timer starts automatically when
|
||||
* added to the simulator backend (@see SimulatorController::addEvent)
|
||||
* @param timeout the time intervall in milliseconds (ms)
|
||||
* @param once \c true, if the TimerEvent should be triggered once,
|
||||
* \c false if it should occur regularly
|
||||
*/
|
||||
TimerEvent(unsigned timeout, bool once)
|
||||
: m_Timeout(timeout), m_Id(-1), m_Once(once) { }
|
||||
~TimerEvent() { }
|
||||
/**
|
||||
* Retrieves the internal timer id. Maybe useful for debug output.
|
||||
* @return the timer id
|
||||
*/
|
||||
timer_id_t getId() const { return m_Id; }
|
||||
/**
|
||||
* Sets the internal timer id. This should not be used by the experiment.
|
||||
* @param id the new timer id, given by the underlying simulator-backend
|
||||
*/
|
||||
void setId(timer_id_t id) { m_Id = id; }
|
||||
/**
|
||||
* Retrieves the timer's timeout value.
|
||||
* @return the timout in milliseconds
|
||||
*/
|
||||
unsigned getTimeout() const { return m_Timeout; }
|
||||
/**
|
||||
* Checks whether the timer occurs once or repetitive.
|
||||
* @return \c true if timer triggers once, \c false if repetitive
|
||||
*/
|
||||
bool getOnceFlag() const { return m_Once; }
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __EVENT_HPP__
|
||||
187
src/core/sal/EventList.cc
Normal file
187
src/core/sal/EventList.cc
Normal file
@ -0,0 +1,187 @@
|
||||
#include <set>
|
||||
|
||||
#include "EventList.hpp"
|
||||
#include "SALInst.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
EventId EventList::add(BaseEvent* ev, ExperimentFlow* pExp)
|
||||
{
|
||||
assert(ev != NULL && "FATAL ERROR: Event (of base type BaseEvent*) cannot be NULL!");
|
||||
// a zero counter does not make sense
|
||||
assert(ev->getCounter() != 0);
|
||||
ev->setParent(pExp); // event is linked to experiment flow
|
||||
|
||||
BPEvent *bp_ev;
|
||||
if((bp_ev = dynamic_cast<BPEvent*>(ev)) != NULL)
|
||||
m_Bp_cache.add(bp_ev);
|
||||
m_BufferList.push_back(ev);
|
||||
return (ev->getId());
|
||||
}
|
||||
|
||||
void EventList::remove(BaseEvent* ev)
|
||||
{
|
||||
// possible cases:
|
||||
// - ev == 0 -> remove all events
|
||||
// * clear m_BufferList
|
||||
// * copy m_FireList to m_DeleteList
|
||||
if (ev == 0) {
|
||||
for (bufferlist_t::iterator it = m_BufferList.begin(); it != m_BufferList.end(); it++)
|
||||
simulator.onEventDeletion(*it);
|
||||
for (firelist_t::iterator it = m_FireList.begin(); it != m_FireList.end(); it++)
|
||||
simulator.onEventDeletion(*it);
|
||||
m_Bp_cache.clear();
|
||||
m_BufferList.clear();
|
||||
// all remaining active events must not fire anymore
|
||||
m_DeleteList.insert(m_DeleteList.end(), m_FireList.begin(), m_FireList.end());
|
||||
|
||||
// - ev != 0 -> remove single event
|
||||
// * find/remove ev in m_BufferList
|
||||
// * if ev in m_FireList, copy to m_DeleteList
|
||||
} else {
|
||||
simulator.onEventDeletion(ev);
|
||||
|
||||
BPEvent *bp_ev;
|
||||
if((bp_ev = dynamic_cast<BPEvent*>(ev)) != NULL)
|
||||
m_Bp_cache.remove(bp_ev);
|
||||
m_BufferList.remove(ev);
|
||||
firelist_t::const_iterator it =
|
||||
std::find(m_FireList.begin(), m_FireList.end(), ev);
|
||||
if (it != m_FireList.end()) {
|
||||
m_DeleteList.push_back(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EventList::iterator EventList::remove(iterator it)
|
||||
{
|
||||
return (m_remove(it, false));
|
||||
}
|
||||
|
||||
EventList::iterator EventList::m_remove(iterator it, bool skip_deletelist)
|
||||
{
|
||||
if (!skip_deletelist) {
|
||||
// If skip_deletelist = true, m_remove was called from makeActive. Accordingly, we
|
||||
// are not going to delete an event, instead we are "moving" an event object (= *it)
|
||||
// from the buffer list to the fire-list. Therefor we only need to call the simulator's
|
||||
// event handler (m_onEventDeletion), if m_remove is called with the primary intention
|
||||
// to *delete* (not "move") an event.
|
||||
simulator.onEventDeletion(*it);
|
||||
m_DeleteList.push_back(*it);
|
||||
}
|
||||
|
||||
BPEvent *bp_ev;
|
||||
if((bp_ev = dynamic_cast<BPEvent*>(*it)) != NULL)
|
||||
m_Bp_cache.remove(bp_ev);
|
||||
return (m_BufferList.erase(it));
|
||||
}
|
||||
|
||||
void EventList::remove(ExperimentFlow* flow)
|
||||
{
|
||||
// WARNING: (*it) (= all elements in the lists) can be an invalid ptr because
|
||||
// clearEvents will be called automatically when the allocating experiment (i.e.
|
||||
// run()) has already ended. Accordingly, we cannot call
|
||||
// simulator.onEventDeletion(*it)
|
||||
// because a dynamic-cast of *it would cause a SEGFAULT. Therefor we require the
|
||||
// experiment flow to remove all residual events by calling clearEvents() (with-
|
||||
// in run()). As a consequence, we are now allowed to call the event-handler here.
|
||||
// See ExperimentFlow.hpp for more details.
|
||||
|
||||
// all events?
|
||||
if (flow == 0) {
|
||||
for (bufferlist_t::iterator it = m_BufferList.begin();
|
||||
it != m_BufferList.end(); it++)
|
||||
simulator.onEventDeletion(*it); // invoke event handler
|
||||
m_Bp_cache.clear();
|
||||
m_BufferList.clear();
|
||||
} else { // remove all events corresponding to a specific experiment ("flow"):
|
||||
for (bufferlist_t::iterator it = m_BufferList.begin();
|
||||
it != m_BufferList.end(); ) {
|
||||
if ((*it)->getParent() == flow) {
|
||||
simulator.onEventDeletion(*it);
|
||||
it = m_BufferList.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
// events that just fired / are about to fire ...
|
||||
for (firelist_t::const_iterator it = m_FireList.begin();
|
||||
it != m_FireList.end(); it++) {
|
||||
if (std::find(m_DeleteList.begin(), m_DeleteList.end(), *it)
|
||||
!= m_DeleteList.end()) {
|
||||
continue; // (already in the delete-list? -> skip!)
|
||||
}
|
||||
// ... need to be pushed into m_DeleteList, as we're currently
|
||||
// iterating over m_FireList in fireActiveEvents() and cannot modify it
|
||||
if (flow == 0 || (*it)->getParent() == flow) {
|
||||
simulator.onEventDeletion(*it);
|
||||
m_DeleteList.push_back(*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EventList::~EventList()
|
||||
{
|
||||
// nothing to do here yet
|
||||
}
|
||||
|
||||
BaseEvent* EventList::getEventFromId(EventId id)
|
||||
{
|
||||
// Loop through all events:
|
||||
for(bufferlist_t::iterator it = m_BufferList.begin();
|
||||
it != m_BufferList.end(); it++)
|
||||
if((*it)->getId() == id)
|
||||
return (*it);
|
||||
return (NULL); // Nothing found.
|
||||
}
|
||||
|
||||
EventList::iterator EventList::makeActive(iterator it)
|
||||
{
|
||||
assert(it != m_BufferList.end() &&
|
||||
"FATAL ERROR: Iterator has already reached the end!");
|
||||
BaseEvent* ev = *it;
|
||||
assert(ev && "FATAL ERROR: Event object pointer cannot be NULL!");
|
||||
ev->decreaseCounter();
|
||||
if (ev->getCounter() > 0) {
|
||||
return ++it;
|
||||
}
|
||||
ev->resetCounter();
|
||||
// Note: This is the one and only situation in which remove() should NOT
|
||||
// store the removed item in the delete-list.
|
||||
iterator it_next = m_remove(it, true); // remove event from buffer-list
|
||||
m_FireList.push_back(ev);
|
||||
return (it_next);
|
||||
}
|
||||
|
||||
void EventList::fireActiveEvents()
|
||||
{
|
||||
for (firelist_t::iterator it = m_FireList.begin();
|
||||
it != m_FireList.end(); it++) {
|
||||
if (std::find(m_DeleteList.begin(), m_DeleteList.end(), *it)
|
||||
== m_DeleteList.end()) { // not found in delete-list?
|
||||
m_pFired = *it;
|
||||
// Inform (call) the simulator's (internal) event handler that we are about
|
||||
// to trigger an event (*before* we actually toggle the experiment flow):
|
||||
simulator.onEventTrigger(m_pFired);
|
||||
ExperimentFlow* pFlow = m_pFired->getParent();
|
||||
assert(pFlow && "FATAL ERROR: The event has no parent experiment (owner)!");
|
||||
simulator.m_Flows.toggle(pFlow);
|
||||
}
|
||||
}
|
||||
m_FireList.clear();
|
||||
m_DeleteList.clear();
|
||||
// Note: Do NOT call any event handlers here!
|
||||
}
|
||||
|
||||
size_t EventList::getContextCount() const
|
||||
{
|
||||
std::set<ExperimentFlow*> uniqueFlows; // count unique ExperimentFlow-ptr
|
||||
for(bufferlist_t::const_iterator it = m_BufferList.begin();
|
||||
it != m_BufferList.end(); it++)
|
||||
uniqueFlows.insert((*it)->getParent());
|
||||
|
||||
return uniqueFlows.size();
|
||||
}
|
||||
|
||||
} // end-of-namespace: fail
|
||||
193
src/core/sal/EventList.hpp
Normal file
193
src/core/sal/EventList.hpp
Normal file
@ -0,0 +1,193 @@
|
||||
#ifndef __EVENT_LIST_HPP__
|
||||
#define __EVENT_LIST_HPP__
|
||||
|
||||
#include <cassert>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Event.hpp"
|
||||
#include "BufferCache.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
class ExperimentFlow;
|
||||
|
||||
/**
|
||||
* Buffer-list for a specific experiment; acts as a simple storage container
|
||||
* for events to watch for:
|
||||
*/
|
||||
typedef std::list<BaseEvent*> bufferlist_t;
|
||||
/**
|
||||
* List of events that match the current simulator event; these events will
|
||||
* be triggered next (the list is used temporarily).
|
||||
*/
|
||||
typedef std::vector<BaseEvent*> firelist_t;
|
||||
/**
|
||||
* List of events that have been deleted during a toggled experiment flow while
|
||||
* triggering the events in the fire-list. This list is used to skip already
|
||||
* deleted events (therefore it is used temporarily either).
|
||||
*/
|
||||
typedef std::vector<BaseEvent*> deletelist_t;
|
||||
|
||||
/**
|
||||
* \class EventList
|
||||
*
|
||||
* \brief This class manages the events of the Fail* implementation.
|
||||
*
|
||||
* If a event is triggered, the internal data structure will be updated (id est,
|
||||
* the event will be removed from the so called buffer-list and added to the
|
||||
* fire-list). Additionaly, if an experiment-flow deletes an "active" event
|
||||
* which is currently stored in the fire-list, the event (to be removed) will
|
||||
* be added to a -so called- delete-list. This ensures to prevent triggering
|
||||
* "active" events which have already been deleted by a previous experiment
|
||||
* flow. (See makeActive() and fireActiveEvent() for implementation specific
|
||||
* details.) EventList is part of the SimulatorController and "outsources"
|
||||
* it's event management.
|
||||
*/
|
||||
class EventList
|
||||
{
|
||||
private:
|
||||
// TODO: List separation of "critical types"? Hashing/sorted lists? (-> performance!)
|
||||
bufferlist_t m_BufferList; //!< the storage for events added by exp.
|
||||
firelist_t m_FireList; //!< the active events (used temporarily)
|
||||
deletelist_t m_DeleteList; //!< the deleted events (used temporarily)
|
||||
BaseEvent* m_pFired; //!< the recently fired Event-object
|
||||
BufferCache<BPEvent*> m_Bp_cache;
|
||||
public:
|
||||
/**
|
||||
* The iterator of this class used to loop through the list of
|
||||
* added events. To retrieve an iterator to the first element, call
|
||||
* begin(). end() returns the iterator, pointing after the last element.
|
||||
* (This behaviour equals the STL iterator in C++.)
|
||||
*/
|
||||
typedef bufferlist_t::iterator iterator;
|
||||
|
||||
EventList() : m_pFired(NULL) { }
|
||||
~EventList();
|
||||
/**
|
||||
* Adds the specified event object for the given ExperimentFlow to the
|
||||
* list of events to be watched for.
|
||||
* @param ev pointer to the event object to be added (cannot be \c NULL)
|
||||
* @param pExp the event context (a pointer to the experiment object
|
||||
* which is interested in such events, cannot be \c NULL)
|
||||
* @return the id of the added event object, that is ev->getId()
|
||||
*/
|
||||
EventId add(BaseEvent* ev, ExperimentFlow* pExp);
|
||||
/**
|
||||
* Removes the event based upon the specified \a ev pointer (requires
|
||||
* to loop through the whole buffer-list).
|
||||
* @param ev the pointer of the event to be removed; if ev is set to
|
||||
* \c NULL, all events (for \a all experiments) will be
|
||||
* removed
|
||||
*/
|
||||
void remove(BaseEvent* ev);
|
||||
/**
|
||||
* Behaves like remove(BaseEvent) and additionally updates the provided
|
||||
* iteration.
|
||||
* @return the updated iterator which will point to the next element
|
||||
*/
|
||||
iterator remove(iterator it);
|
||||
private:
|
||||
/**
|
||||
* Internal implementation of remove(iterator it) that allows
|
||||
* to skip the delete-list.
|
||||
* @return the updated iterator which will point to the next element
|
||||
*/
|
||||
iterator m_remove(iterator it, bool skip_deletelist);
|
||||
public:
|
||||
/**
|
||||
* Returns an iterator to the beginning of the internal data structure.
|
||||
* Don't forget to update the returned iterator when calling one of the
|
||||
* modifying methods like makeActive() or remove(). Therefore you need
|
||||
* to call the iterator-based variants of makeActive() and remove().
|
||||
* \code
|
||||
* [X|1|2| ... |n]
|
||||
* ^
|
||||
* \endcode
|
||||
*/
|
||||
iterator begin() { return (m_BufferList.begin()); }
|
||||
/**
|
||||
* Returns an iterator to the end of the interal data structure.
|
||||
* Don't forget to update the returned iterator when calling one of the
|
||||
* modifying methods like makeActive() or remove(). Therefore you need
|
||||
* to call the iterator-based variants of makeActive() and remove().
|
||||
* \code
|
||||
* [1|2| ... |n]X
|
||||
* ^
|
||||
* \endcode
|
||||
*/
|
||||
iterator end() { return (m_BufferList.end()); }
|
||||
/**
|
||||
* Retrieves the event object for the given \a id. The internal data
|
||||
* remains unchanged.
|
||||
* @param id of event to be retrieved.
|
||||
* @return pointer to event or \c NULL of \a id could not be found
|
||||
*/
|
||||
BaseEvent* getEventFromId(EventId id);
|
||||
/**
|
||||
* Removes all events for the specified experiment.
|
||||
* @param flow pointer to experiment context (0 = all experiments)
|
||||
*/
|
||||
void remove(ExperimentFlow* flow);
|
||||
/**
|
||||
* Retrieves the number of experiments which currently have active
|
||||
* events. This number is trivially equal to the (current) total
|
||||
* number of ExperimentFlow-objects.
|
||||
* @return number of experiments having active events
|
||||
*/
|
||||
size_t getContextCount() const;
|
||||
/**
|
||||
* Retrieves the total number of buffered events. This doesn't include
|
||||
* the events in the fire- or delete-list.
|
||||
* @return the total event count (for all flows)
|
||||
*/
|
||||
size_t getEventCount() const { return m_BufferList.size(); }
|
||||
/**
|
||||
* Retrieves the recently triggered event object. To map this object to
|
||||
* it's context (id est, the related ExerimentFlow), use
|
||||
* \c getLastFiredDest().
|
||||
* @return a pointer to the recent event or \c NULL if nothing has been
|
||||
* triggered so far
|
||||
*/
|
||||
BaseEvent* getLastFired() { return (m_pFired); }
|
||||
/**
|
||||
* Retrieves the ExperimentFlow-object for the given BaseEvent (it's
|
||||
* \a context).
|
||||
* @param pEv the event object to be looked up
|
||||
* @return a pointer to the context of \a pEv or \c NULL if the
|
||||
* corresponding context could not be found
|
||||
*/
|
||||
ExperimentFlow* getExperimentOf(BaseEvent* pEv);
|
||||
/**
|
||||
* Moves the events from the (internal) buffer-list to the fire-list.
|
||||
* To actually fire the events, call fireActiveEvents().
|
||||
* Returns an updated iterator which points to the next element.
|
||||
* @param ev the event to trigger
|
||||
* @return returns the updated iteration, pointing to the next element
|
||||
* after makeActive returns, "it" is invalid, so the returned
|
||||
* iterator should be used to continue the iteration
|
||||
*
|
||||
* TODO: Improve naming (instead of "makeActive")?
|
||||
*/
|
||||
iterator makeActive(iterator it);
|
||||
/**
|
||||
* Triggers the active events. Each event is triggered if it has not
|
||||
* recently been removed (id est: is not found in the delete-list). See
|
||||
* makeActive() for more details. The recently triggered event can be
|
||||
* retrieved by calling \a getLastFired(). After all events have been
|
||||
* triggered, the (internal) fire- and delete-list will be cleared.
|
||||
*
|
||||
* TODO: Improve naming (instead of "fireActiveEvents")?
|
||||
*/
|
||||
void fireActiveEvents();
|
||||
/**
|
||||
* Retrieves the BPEvent buffer cache.
|
||||
* @returns the buffer cache
|
||||
*/
|
||||
inline BufferCache<BPEvent*> *getBPBuffer() { return &m_Bp_cache; }
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __EVENT_LIST_HPP__
|
||||
9
src/core/sal/Memory.cc
Normal file
9
src/core/sal/Memory.cc
Normal file
@ -0,0 +1,9 @@
|
||||
#include <cstdlib>
|
||||
|
||||
#include "Memory.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
const guest_address_t ADDR_INV = 0;
|
||||
|
||||
}
|
||||
74
src/core/sal/Memory.hpp
Normal file
74
src/core/sal/Memory.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
#ifndef __MEMORY_HPP__
|
||||
#define __MEMORY_HPP__
|
||||
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
#include <cstring> // Added for size_t support
|
||||
|
||||
#include "SALConfig.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class MemoryManager
|
||||
* Defines an abstract interface to access the simulated memory pool
|
||||
* and query meta information, e.g. the memory size.
|
||||
*/
|
||||
class MemoryManager
|
||||
{
|
||||
public:
|
||||
virtual ~MemoryManager() { }
|
||||
/**
|
||||
* Retrieves the size of the available simulated memory.
|
||||
* @return the size of the memory pool in bytes
|
||||
*/
|
||||
virtual size_t getPoolSize() const = 0;
|
||||
/**
|
||||
* Retrieves the starting address of the host memory. This is the
|
||||
* first valid address in memory.
|
||||
* @return the starting address
|
||||
*/
|
||||
virtual host_address_t getStartAddr() const = 0;
|
||||
/**
|
||||
* Retrieves the byte at address \a addr in the memory.
|
||||
* @param addr The guest address where the byte is located.
|
||||
* The address is expected to be valid.
|
||||
* @return the byte at \a addr
|
||||
*/
|
||||
virtual byte_t getByte(guest_address_t addr) = 0;
|
||||
/**
|
||||
* Retrieves \a cnt bytes at address \a addr from the memory.
|
||||
* @param addr The guest address where the bytes are located.
|
||||
* The address is expected to be valid.
|
||||
* @param cnt The number of bytes to be retrieved. \a addr + \a cnt
|
||||
* is expected to not exceed the memory limit.
|
||||
* @param dest Pointer to destination buffer to copy the data to.
|
||||
*/
|
||||
virtual void getBytes(guest_address_t addr, size_t cnt, void *dest) = 0;
|
||||
/**
|
||||
* Writes the byte \a data to memory.
|
||||
* @param addr The guest address to write.
|
||||
* The address is expected to be valid.
|
||||
* @param data The new byte to write
|
||||
*/
|
||||
virtual void setByte(guest_address_t addr, byte_t data) = 0;
|
||||
/**
|
||||
* Copies data to memory.
|
||||
* @param addr The guest address to write.
|
||||
* The address is expected to be valid.
|
||||
* @param cnt The number of bytes to be retrieved. \a addr + \a cnt
|
||||
* is expected to not exceed the memory limit.
|
||||
* @param src Pointer to data to be copied.
|
||||
*/
|
||||
virtual void setBytes(guest_address_t addr, size_t cnt, void const *src) = 0;
|
||||
/**
|
||||
* Transforms the guest address \a addr to a host address.
|
||||
* @param addr The guest address to be transformed
|
||||
* @return the transformed (host) address or \c ADDR_INV on errors
|
||||
*/
|
||||
virtual host_address_t guestToHost(guest_address_t addr) = 0;
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __MEMORY_HPP__
|
||||
69
src/core/sal/Register.cc
Normal file
69
src/core/sal/Register.cc
Normal file
@ -0,0 +1,69 @@
|
||||
#include "Register.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
Register* UniformRegisterSet::getRegister(size_t i)
|
||||
{
|
||||
assert(i < m_Regs.size() && "FATAL ERROR: Invalid index provided!");
|
||||
return (m_Regs[i]);
|
||||
}
|
||||
|
||||
Register* RegisterManager::getRegister(size_t i)
|
||||
{
|
||||
assert(i < m_Registers.size() && "FATAL ERROR: Invalid index provided!");
|
||||
return (m_Registers[i]);
|
||||
}
|
||||
|
||||
void RegisterManager::add(Register* reg)
|
||||
{
|
||||
assert(!reg->isAssigned() && "FATAL ERROR: The register is already assigned!");
|
||||
m_Registers.push_back(reg);
|
||||
|
||||
UniformRegisterSet* urs = getSetOfType(reg->getType());
|
||||
if(urs == NULL) {
|
||||
urs = new UniformRegisterSet(reg->getType());
|
||||
m_Subsets.push_back(urs);
|
||||
}
|
||||
urs->m_add(reg);
|
||||
}
|
||||
|
||||
void UniformRegisterSet::m_add(Register* preg)
|
||||
{
|
||||
assert(!preg->m_Assigned &&
|
||||
"FATAL ERROR: The register has already been assigned.");
|
||||
m_Regs.push_back(preg);
|
||||
preg->m_Assigned = true;
|
||||
preg->m_Index = m_Regs.size()-1; // the index within the vector (set)
|
||||
}
|
||||
|
||||
size_t RegisterManager::count() const
|
||||
{
|
||||
return (m_Registers.size());
|
||||
}
|
||||
|
||||
UniformRegisterSet& RegisterManager::getSet(size_t i)
|
||||
{
|
||||
assert(i < m_Subsets.size() && "FATAL ERROR: Invalid index provided!");
|
||||
return (*m_Subsets[i]);
|
||||
}
|
||||
|
||||
UniformRegisterSet* RegisterManager::getSetOfType(RegisterType t)
|
||||
{
|
||||
for(std::vector< UniformRegisterSet* >::iterator it = m_Subsets.begin();
|
||||
it != m_Subsets.end(); it++)
|
||||
{
|
||||
if((*it)->getType() == t)
|
||||
return (*it);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void RegisterManager::clear()
|
||||
{
|
||||
for(std::vector< UniformRegisterSet* >::iterator it = m_Subsets.begin();
|
||||
it != m_Subsets.end(); it++)
|
||||
delete (*it);
|
||||
m_Subsets.clear();
|
||||
}
|
||||
|
||||
} // end-of-namespace: fail
|
||||
278
src/core/sal/Register.hpp
Normal file
278
src/core/sal/Register.hpp
Normal file
@ -0,0 +1,278 @@
|
||||
#ifndef __REGISTER_HPP__
|
||||
#define __REGISTER_HPP__
|
||||
|
||||
#include <vector>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "SALConfig.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \enum RegisterType
|
||||
*
|
||||
* Lists the different register types. You need to expand this enumeration
|
||||
* to provide more detailed types for your concrete derived register classes
|
||||
* specific to a simulator.
|
||||
*/
|
||||
enum RegisterType
|
||||
{
|
||||
RT_GP, //!< general purpose
|
||||
RT_PC, //!< program counter / instruction pointer
|
||||
RT_ST //!< status register
|
||||
};
|
||||
|
||||
/**
|
||||
* \class Register
|
||||
*
|
||||
* Represents the basic generic register class. A set of registers is composed
|
||||
* of classes which had been derived from this class.
|
||||
*/
|
||||
class Register
|
||||
{
|
||||
protected:
|
||||
RegisterType m_Type; //!< the type of this register
|
||||
regwidth_t m_Width; //!< the register width
|
||||
unsigned int m_Id; //!< the unique id of this register
|
||||
//! \c true if register has already been assigned, \c false otherwise
|
||||
bool m_Assigned;
|
||||
//! The index in it's register set if assigned (\c -1 otherwise)
|
||||
size_t m_Index;
|
||||
std::string m_Name; //!< The (optional) name, maybe empty
|
||||
friend class UniformRegisterSet;
|
||||
public:
|
||||
/**
|
||||
* Creates a new register.
|
||||
* @param id the unique id of this register (simulator specific)
|
||||
* @param t the type of the register to be constructed
|
||||
* @param w the width of the register in bits
|
||||
*/
|
||||
Register(unsigned int id, RegisterType t, regwidth_t w)
|
||||
: m_Type(t), m_Width(w), m_Id(id), m_Assigned(false),
|
||||
m_Index(static_cast<size_t>(-1)) { }
|
||||
/**
|
||||
* Returns the (fixed) type of this register.
|
||||
* @return the type of this register
|
||||
*/
|
||||
RegisterType getType() const { return (m_Type); }
|
||||
/**
|
||||
* Returns the (fixed) width of this register.
|
||||
* @return the width in bits
|
||||
*/
|
||||
regwidth_t getWidth() const { return (m_Width); }
|
||||
/**
|
||||
* Returns the data referenced by this register. In a concrete
|
||||
* derived class this method has to be defined appropriately.
|
||||
* @return the current data
|
||||
*/
|
||||
virtual regdata_t getData() /*!const*/ = 0;
|
||||
/**
|
||||
* Sets new data to be stored in this register.
|
||||
* @param data the data to be written to the register
|
||||
*/
|
||||
virtual void setData(regdata_t data) = 0;
|
||||
/**
|
||||
* Sets the (optional) name of this register.
|
||||
* @param name the textual register name, e.g. "EAX"
|
||||
*/
|
||||
void setName(const std::string& name) { m_Name = name; }
|
||||
/**
|
||||
* Retrieves the register name.
|
||||
* @return the textual register description
|
||||
*/
|
||||
const std::string& getName() const { return (m_Name); }
|
||||
/**
|
||||
* Retrieves the unique index within it's assigned register set.
|
||||
* If the register has not been assigned, \c (size_t)-1 will be
|
||||
* returned.
|
||||
* @return the register index or -1 if not assigned
|
||||
* @see isAssigned()
|
||||
*/
|
||||
size_t getIndex() const { return (m_Index); }
|
||||
/**
|
||||
* Checks whether this register has already been assigned. On
|
||||
* creation the register isn't initially assigned.
|
||||
* @return \c true if assigned, \c false otherwise
|
||||
*/
|
||||
bool isAssigned() const { return (m_Assigned); }
|
||||
/**
|
||||
* Returns the unique id of this register.
|
||||
* @return the unique id
|
||||
*/
|
||||
unsigned int getId() const { return (m_Id); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class UniformRegisterSet
|
||||
*
|
||||
* Represents a (type-uniform) set of registers, e.g. all general purpose
|
||||
* registers. The granularity of the register type is determined by the
|
||||
* enumeration \c RegisterType. (All registers within this set must be of the
|
||||
* same register type.) The capacity of the set is managed automatically.
|
||||
*/
|
||||
class UniformRegisterSet
|
||||
{
|
||||
private:
|
||||
std::vector< Register* > m_Regs; //!< the unique set of registers
|
||||
RegisterType m_Type; //!< the overall type of this container (set)
|
||||
void m_add(Register* preg);
|
||||
friend class RegisterManager;
|
||||
public:
|
||||
/**
|
||||
* The iterator of this class used to loop through the list of
|
||||
* added registers. To retrieve an iterator to the first element, call
|
||||
* begin(). end() returns the iterator, pointing after the last element.
|
||||
* (This behaviour equals the STL iterator in C++.)
|
||||
*/
|
||||
typedef std::vector< Register* >::iterator iterator;
|
||||
/**
|
||||
* Returns an iterator to the beginning of the internal data structure.
|
||||
* \code
|
||||
* [1|2| ... |n]
|
||||
* ^
|
||||
* \endcode
|
||||
*/
|
||||
iterator begin() { return (m_Regs.begin()); }
|
||||
/**
|
||||
* Returns an iterator to the end of the interal data structure.
|
||||
* \code
|
||||
* [1|2| ... |n]X
|
||||
* ^
|
||||
* \endcode
|
||||
*/
|
||||
iterator end() { return (m_Regs.end()); }
|
||||
/**
|
||||
* Constructs a new register set with type \a containerType.
|
||||
* @param containerType the type of registers which should be stored
|
||||
* in this set
|
||||
*/
|
||||
UniformRegisterSet(RegisterType containerType)
|
||||
: m_Type(containerType) { }
|
||||
/**
|
||||
* Returns the type of this set.
|
||||
* @return the type
|
||||
*/
|
||||
RegisterType getType() const { return (m_Type); }
|
||||
/**
|
||||
* Gets the number of registers of this set.
|
||||
* @return the number of registers
|
||||
*/
|
||||
size_t count() const { return (m_Regs.size()); }
|
||||
/**
|
||||
* Retrieves the \a i-th register within this set.
|
||||
* @return a pointer to the \a i-th register; if \a i is invalid, an
|
||||
* assertion is thrown
|
||||
*/
|
||||
Register* getRegister(size_t i);
|
||||
/**
|
||||
* Retrieves the first register within this set (syntactical sugar).
|
||||
* @return a pointer to the first register (if existing -- otherwise an
|
||||
* assertion is thrown)
|
||||
*/
|
||||
virtual Register* first() { return (getRegister(0)); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class RegisterManager
|
||||
* Represents a complete set of (inhomogeneous) registers specific to a concrete
|
||||
* architecture, e.g. x86 or ARM.
|
||||
*/
|
||||
class RegisterManager
|
||||
{
|
||||
protected:
|
||||
std::vector< Register* > m_Registers;
|
||||
//!< the managed set of homogeneous sets
|
||||
std::vector< UniformRegisterSet* > m_Subsets;
|
||||
public:
|
||||
/**
|
||||
* The iterator of this class used to loop through the list of
|
||||
* added registers. To retrieve an iterator to the first element, call
|
||||
* begin(). end() returns the iterator, pointing after the last element.
|
||||
* (This behaviour equals the STL iterator in C++.)
|
||||
*/
|
||||
typedef std::vector< Register* >::iterator iterator;
|
||||
/**
|
||||
* Returns an iterator to the beginning of the internal data structure.
|
||||
* \code
|
||||
* [1|2| ... |n]
|
||||
* ^
|
||||
* \endcode
|
||||
*/
|
||||
iterator begin() { return (m_Registers.begin()); }
|
||||
/**
|
||||
* Returns an iterator to the end of the interal data structure.
|
||||
* \code
|
||||
* [1|2| ... |n]X
|
||||
* ^
|
||||
* \endcode
|
||||
*/
|
||||
iterator end() { return (m_Registers.end()); }
|
||||
|
||||
RegisterManager() { }
|
||||
~RegisterManager() { clear(); }
|
||||
/**
|
||||
* Retrieves the total number of registers over all homogeneous sets.
|
||||
* @return the total register count
|
||||
*/
|
||||
virtual size_t count() const;
|
||||
/**
|
||||
* Retrieves the number of managed homogeneous register sets.
|
||||
* @return the number of sets
|
||||
*/
|
||||
virtual size_t subsetCount() const { return (m_Subsets.size()); }
|
||||
/**
|
||||
* Gets the \a i-th register set.
|
||||
* @param i the index of the set to be returned
|
||||
* @return a reference to the uniform register set
|
||||
* @see subsetCount()
|
||||
*/
|
||||
virtual UniformRegisterSet& getSet(size_t i);
|
||||
/**
|
||||
* Returns the set with register type \a t. The set can be used to
|
||||
* loop over all registers of type \a t.
|
||||
* @param t the type to check for
|
||||
* @return a pointer to the retrieved register set (if found), NULL
|
||||
* otherwise
|
||||
*/
|
||||
virtual UniformRegisterSet* getSetOfType(RegisterType t);
|
||||
/**
|
||||
* Adds a new register to this set. The register object needs to be
|
||||
* typed (see Register::getType).
|
||||
* @param reg a pointer to the register object to be added
|
||||
* @see getType()
|
||||
*/
|
||||
void add(Register* reg);
|
||||
/**
|
||||
* Retrieves the \a i-th register.
|
||||
* @return a pointer to the \a i-th register; if \a i is invalid, an
|
||||
* assertion is thrown
|
||||
*/
|
||||
Register* getRegister(size_t i);
|
||||
/**
|
||||
* Removes all registers and sets from the RegisterManager.
|
||||
*/
|
||||
virtual void clear();
|
||||
/**
|
||||
* Returns the current instruction pointer.
|
||||
* @return the current eip
|
||||
*/
|
||||
virtual address_t getInstructionPointer() = 0;
|
||||
/**
|
||||
* Retruns the top address of the stack.
|
||||
* @return the starting address of the stack
|
||||
*/
|
||||
virtual address_t getStackPointer() = 0;
|
||||
/**
|
||||
* Retrieves the base ptr (holding the address of the
|
||||
* current stack frame).
|
||||
* @return the base pointer
|
||||
*/
|
||||
virtual address_t getBasePointer() = 0;
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __REGISTER_HPP__
|
||||
29
src/core/sal/SALConfig.hpp
Normal file
29
src/core/sal/SALConfig.hpp
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef __SAL_CONFIG_HPP__
|
||||
#define __SAL_CONFIG_HPP__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config/VariantConfig.hpp"
|
||||
|
||||
// Type-config depends on the current selected simulator:
|
||||
#if defined BUILD_BOCHS
|
||||
#include "bochs/BochsConfig.hpp"
|
||||
#elif defined BUILD_OVP
|
||||
#include "ovp/OVPConfig.hpp"
|
||||
#else
|
||||
#error SAL Config Target not defined
|
||||
#endif
|
||||
|
||||
namespace fail {
|
||||
|
||||
typedef guest_address_t address_t; //!< common address type to be used in experiment flows
|
||||
typedef uint8_t byte_t; //!< 8 bit type for memory access (read or write)
|
||||
typedef uint32_t regwidth_t; //!< type of register width [bits]
|
||||
typedef register_data_t regdata_t; //!< type of register data
|
||||
typedef timer_t timer_id_t; //!< type of timer IDs
|
||||
|
||||
extern const address_t ADDR_INV; //!< invalid address flag (defined in Memory.cc)
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __SAL_CONFIG_HPP__
|
||||
33
src/core/sal/SALInst.hpp
Normal file
33
src/core/sal/SALInst.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef __SAL_INSTANCE_HPP__
|
||||
#define __SAL_INSTANCE_HPP__
|
||||
|
||||
#include "SALConfig.hpp"
|
||||
#include "config/VariantConfig.hpp"
|
||||
|
||||
#ifdef BUILD_BOCHS
|
||||
|
||||
#include "bochs/BochsController.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
typedef BochsController ConcreteSimulatorController; //!< concrete simulator (type)
|
||||
extern ConcreteSimulatorController simulator; //!< the global simulator-controller instance
|
||||
|
||||
}
|
||||
|
||||
#elif defined BUILD_OVP
|
||||
|
||||
#include "ovp/OVPController.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
typedef OVPController ConcreteSimulatorController; //!< concrete simulator (type)
|
||||
extern ConcreteSimulatorController simulator; //!< the global simulator-controller instance
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
#error SAL Instance not defined
|
||||
#endif
|
||||
|
||||
#endif // __SAL_INSTANCE_HPP__
|
||||
252
src/core/sal/SimulatorController.cc
Normal file
252
src/core/sal/SimulatorController.cc
Normal file
@ -0,0 +1,252 @@
|
||||
#include "SimulatorController.hpp"
|
||||
#include "SALInst.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
// External reference declared in SALInst.hpp
|
||||
ConcreteSimulatorController simulator;
|
||||
|
||||
EventId SimulatorController::addEvent(BaseEvent* ev)
|
||||
{
|
||||
assert(ev != NULL && "FATAL ERROR: Argument (ptr) cannot be NULL!");
|
||||
EventId ret = m_EvList.add(ev, m_Flows.getCurrent());
|
||||
// Call the common postprocessing function:
|
||||
if (!onEventAddition(ev)) { // If the return value signals "false"...,
|
||||
m_EvList.remove(ev); // ...skip the addition
|
||||
ret = INVALID_EVENT;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
BaseEvent* SimulatorController::waitAny(void)
|
||||
{
|
||||
m_Flows.resume();
|
||||
assert(m_EvList.getLastFired() != NULL &&
|
||||
"FATAL ERROR: getLastFired() expected to be non-NULL!");
|
||||
return (m_EvList.getLastFired());
|
||||
}
|
||||
|
||||
void SimulatorController::startup()
|
||||
{
|
||||
// Some greetings to the user:
|
||||
std::cout << "[SimulatorController] Initializing..." << std::endl;
|
||||
// TODO: Log-Level?
|
||||
|
||||
// Activate previously added experiments to allow initialization:
|
||||
initExperiments();
|
||||
}
|
||||
|
||||
void SimulatorController::initExperiments()
|
||||
{
|
||||
/* empty. */
|
||||
}
|
||||
|
||||
void SimulatorController::onBreakpointEvent(address_t instrPtr, address_t address_space)
|
||||
{
|
||||
assert(false &&
|
||||
"FIXME: SimulatorController::onBreakpointEvent() has not been tested before");
|
||||
|
||||
// FIXME: Improve performance
|
||||
|
||||
// Loop through all events of type BP*Event:
|
||||
EventList::iterator it = m_EvList.begin();
|
||||
while (it != m_EvList.end())
|
||||
{
|
||||
BaseEvent* pev = *it;
|
||||
BPSingleEvent* pbp; BPRangeEvent* pbpr;
|
||||
if((pbp = dynamic_cast<BPSingleEvent*>(pev)) && pbp->isMatching(instrPtr, address_space))
|
||||
{
|
||||
pbp->setTriggerInstructionPointer(instrPtr);
|
||||
it = m_EvList.makeActive(it);
|
||||
// "it" has already been set to the next element (by calling
|
||||
// makeActive()):
|
||||
continue; // -> skip iterator increment
|
||||
}
|
||||
else if((pbpr = dynamic_cast<BPRangeEvent*>(pev)) &&
|
||||
pbpr->isMatching(instrPtr, address_space))
|
||||
{
|
||||
pbpr->setTriggerInstructionPointer(instrPtr);
|
||||
it = m_EvList.makeActive(it);
|
||||
continue; // dito
|
||||
}
|
||||
++it;
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
void SimulatorController::onMemoryAccessEvent(address_t addr, size_t len,
|
||||
bool is_write, address_t instrPtr)
|
||||
{
|
||||
// FIXME: Improve performance
|
||||
|
||||
MemAccessEvent::accessType_t accesstype =
|
||||
is_write ? MemAccessEvent::MEM_WRITE
|
||||
: MemAccessEvent::MEM_READ;
|
||||
|
||||
EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end()) // check for active events
|
||||
{
|
||||
BaseEvent* pev = *it;
|
||||
MemAccessEvent* ev = dynamic_cast<MemAccessEvent*>(pev);
|
||||
// Is this a MemAccessEvent? Correct access type?
|
||||
if(!ev || !ev->isMatching(addr, accesstype))
|
||||
{
|
||||
++it;
|
||||
continue; // skip event activation
|
||||
}
|
||||
ev->setTriggerAddress(addr);
|
||||
ev->setTriggerWidth(len);
|
||||
ev->setTriggerInstructionPointer(instrPtr);
|
||||
ev->setTriggerAccessType(accesstype);
|
||||
it = m_EvList.makeActive(it);
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
void SimulatorController::onInterruptEvent(unsigned interruptNum, bool nmi)
|
||||
{
|
||||
EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end()) // check for active events
|
||||
{
|
||||
BaseEvent* pev = *it;
|
||||
InterruptEvent* pie = dynamic_cast<InterruptEvent*>(pev);
|
||||
if(!pie || !pie->isMatching(interruptNum))
|
||||
{
|
||||
++it;
|
||||
continue; // skip event activation
|
||||
}
|
||||
pie->setTriggerNumber(interruptNum);
|
||||
pie->setNMI(nmi);
|
||||
it = m_EvList.makeActive(it);
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
bool SimulatorController::isSuppressedInterrupt(unsigned interruptNum)
|
||||
{
|
||||
for(size_t i = 0; i < m_SuppressedInterrupts.size(); i++)
|
||||
if((m_SuppressedInterrupts[i] == interruptNum ||
|
||||
m_SuppressedInterrupts[i] == ANY_INTERRUPT) && interruptNum != (unsigned) interrupt_to_fire+32 ){
|
||||
if((int)interruptNum == interrupt_to_fire+32){
|
||||
interrupt_to_fire = -1;
|
||||
return(true);
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool SimulatorController::addSuppressedInterrupt(unsigned interruptNum)
|
||||
{
|
||||
// Check if already existing:
|
||||
if(isSuppressedInterrupt(interruptNum+32))
|
||||
return (false); // already added: nothing to do here
|
||||
|
||||
if(interruptNum == ANY_INTERRUPT){
|
||||
m_SuppressedInterrupts.push_back(interruptNum);
|
||||
return (true);
|
||||
}else{
|
||||
m_SuppressedInterrupts.push_back(interruptNum+32);
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
|
||||
bool SimulatorController::removeSuppressedInterrupt(unsigned interruptNum)
|
||||
{
|
||||
for(size_t i = 0; i < m_SuppressedInterrupts.size(); i++)
|
||||
{
|
||||
if(m_SuppressedInterrupts[i] == interruptNum+32 || m_SuppressedInterrupts[i] == ANY_INTERRUPT)
|
||||
{
|
||||
m_SuppressedInterrupts.erase(m_SuppressedInterrupts.begin() + i);
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
void SimulatorController::onTrapEvent(unsigned trapNum)
|
||||
{
|
||||
EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end()) // check for active events
|
||||
{
|
||||
BaseEvent* pev = *it;
|
||||
TrapEvent* pte = dynamic_cast<TrapEvent*>(pev);
|
||||
if(!pte || !pte->isMatching(trapNum))
|
||||
{
|
||||
++it;
|
||||
continue; // skip event activation
|
||||
}
|
||||
pte->setTriggerNumber(trapNum);
|
||||
it = m_EvList.makeActive(it);
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
void SimulatorController::onGuestSystemEvent(char data, unsigned port)
|
||||
{
|
||||
EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end()) // check for active events
|
||||
{
|
||||
BaseEvent* pev = *it;
|
||||
GuestEvent* pge = dynamic_cast<GuestEvent*>(pev);
|
||||
if(pge != NULL)
|
||||
{
|
||||
pge->setData(data);
|
||||
pge->setPort(port);
|
||||
it = m_EvList.makeActive(it);
|
||||
continue; // dito.
|
||||
}
|
||||
++it;
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
void SimulatorController::onJumpEvent(bool flagTriggered, unsigned opcode)
|
||||
{
|
||||
EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end()) // check for active events
|
||||
{
|
||||
JumpEvent* pje = dynamic_cast<JumpEvent*>(*it);
|
||||
if(pje != NULL)
|
||||
{
|
||||
pje->setOpcode(opcode);
|
||||
pje->setFlagTriggered(flagTriggered);
|
||||
it = m_EvList.makeActive(it);
|
||||
continue; // dito.
|
||||
}
|
||||
++it;
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
void SimulatorController::addFlow(ExperimentFlow* flow)
|
||||
{
|
||||
// Store the (flow,corohandle)-tuple internally and create its coroutine:
|
||||
m_Flows.create(flow);
|
||||
// let it run for the first time
|
||||
m_Flows.toggle(flow);
|
||||
}
|
||||
|
||||
void SimulatorController::removeFlow(ExperimentFlow* flow)
|
||||
{
|
||||
// remove all remaining events of this flow
|
||||
clearEvents(flow);
|
||||
// remove coroutine
|
||||
m_Flows.remove(flow);
|
||||
}
|
||||
|
||||
BaseEvent* SimulatorController::addEventAndWait(BaseEvent* ev)
|
||||
{
|
||||
addEvent(ev);
|
||||
return (waitAny());
|
||||
}
|
||||
|
||||
void SimulatorController::terminate(int exCode)
|
||||
{
|
||||
// Attention: This could cause problems, e.g., because of non-closed sockets
|
||||
std::cout << "[FAIL] Exit called by experiment with exit code: " << exCode << std::endl;
|
||||
// TODO: (Non-)Verbose-Mode? Log-Level?
|
||||
exit(exCode);
|
||||
}
|
||||
|
||||
} // end-of-namespace: fail
|
||||
271
src/core/sal/SimulatorController.hpp
Normal file
271
src/core/sal/SimulatorController.hpp
Normal file
@ -0,0 +1,271 @@
|
||||
#ifndef __SIMULATOR_CONTROLLER_HPP__
|
||||
#define __SIMULATOR_CONTROLLER_HPP__
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
|
||||
#include "efw/CoroutineManager.hpp"
|
||||
#include "EventList.hpp"
|
||||
#include "SALConfig.hpp"
|
||||
#include "Event.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
// Incomplete types suffice here:
|
||||
class ExperimentFlow;
|
||||
class RegisterManager;
|
||||
class MemoryManager;
|
||||
|
||||
/**
|
||||
* \class SimulatorController
|
||||
*
|
||||
* \brief The abstract interface for controlling simulators and
|
||||
* accessing experiment data/flows (as part of the "Simulator
|
||||
* Abstraction Layer".
|
||||
*
|
||||
* This class manages (1..N) experiments and provides access to the underlying
|
||||
* simulator/debugger system. Experiments can enlist arbritrary events
|
||||
* (Breakpoint, Memory access, Traps, etc.). The SimulatorController then
|
||||
* activates the specific experiment There are further methods to read/write
|
||||
* registers and memory, and control the SUT (save/restore/reset).
|
||||
*/
|
||||
class SimulatorController
|
||||
{
|
||||
protected:
|
||||
EventList m_EvList; //!< storage where events are being buffered
|
||||
CoroutineManager m_Flows; //!< managed experiment flows
|
||||
RegisterManager *m_Regs; //!< access to cpu register
|
||||
MemoryManager *m_Mem; //!< access to memory pool
|
||||
//! list of suppressed interrupts
|
||||
std::vector<unsigned> m_SuppressedInterrupts;
|
||||
friend class EventList; //!< "outsources" the event management
|
||||
public:
|
||||
SimulatorController()
|
||||
: m_Regs(NULL), m_Mem(NULL) { }
|
||||
SimulatorController(RegisterManager* regs, MemoryManager* mem)
|
||||
: m_Regs(regs), m_Mem(mem) { }
|
||||
virtual ~SimulatorController() { }
|
||||
/**
|
||||
* @brief Initialization function each implementation needs to call on
|
||||
* startup
|
||||
*
|
||||
* This function needs to be invoked once the simulator starts, and
|
||||
* allows the SimulatorController to instantiate all needed experiment
|
||||
* components.
|
||||
*/
|
||||
void startup();
|
||||
/**
|
||||
* Experiments need to hook here.
|
||||
*/
|
||||
void initExperiments();
|
||||
/* ********************************************************************
|
||||
* Standard Event Handler API
|
||||
* ********************************************************************/
|
||||
/**
|
||||
* Breakpoint event handler. This routine needs to be called in the
|
||||
* simulator specific backend each time a breakpoint event occurs.
|
||||
* @param instrPtr the instruction pointer of the breakpoint event
|
||||
* @param address_space the address space it should occur in
|
||||
*/
|
||||
void onBreakpointEvent(address_t instrPtr, address_t address_space);
|
||||
/**
|
||||
* Memory access event handler (read/write).
|
||||
* @param addr the accessed memory address
|
||||
* @param len the length of the accessed memory
|
||||
* @param is_write \c true if memory is written, \c false if read
|
||||
* @param instrPtr the address of the instruction causing the memory
|
||||
* access
|
||||
*
|
||||
* FIXME: should instrPtr be part of this interface?
|
||||
*/
|
||||
void onMemoryAccessEvent(address_t addr, size_t len,
|
||||
bool is_write, address_t instrPtr);
|
||||
/**
|
||||
* Interrupt event handler.
|
||||
* @param interruptNum the interrupt-type id
|
||||
* @param nmi nmi-value from guest-system
|
||||
*/
|
||||
void onInterruptEvent(unsigned interruptNum, bool nmi);
|
||||
/**
|
||||
* Trap event handler.
|
||||
* @param trapNum the trap-type id
|
||||
*/
|
||||
void onTrapEvent(unsigned trapNum);
|
||||
/**
|
||||
* Guest system communication handler.
|
||||
* @param data the "message" from the guest system
|
||||
* @param port the port of the event
|
||||
*/
|
||||
void onGuestSystemEvent(char data, unsigned port);
|
||||
/**
|
||||
* (Conditional) Jump-instruction handler.
|
||||
* @param flagTriggered \c true if the jump was triggered due to a
|
||||
* specific FLAG (zero/carry/sign/overflow/parity flag)
|
||||
* @param opcode the opcode of the conrecete jump instruction
|
||||
*/
|
||||
void onJumpEvent(bool flagTriggered, unsigned opcode);
|
||||
/**
|
||||
* This method is called when an experiment flow adds a new event by
|
||||
* calling \c simulator.addEvent(pev) or \c simulator.addEventAndWait(pev).
|
||||
* More specifically, the event will be added to the event-list first
|
||||
* (buffer-list, to be precise) and then this event handler is called.
|
||||
* @param pev the event which has been added
|
||||
* @return You should return \c true to continue and \c false to prevent
|
||||
* the addition of the event \a pev, yielding an error in the
|
||||
* experiment flow (i.e. -1 is returned).
|
||||
*/
|
||||
virtual bool onEventAddition(BaseEvent* pev) { return true; }
|
||||
/**
|
||||
* This method is called when an experiment flow removes an event from
|
||||
* the event-management by calling \c removeEvent(prev), \c clearEvents()
|
||||
* or by deleting a complete flow (\c removeFlow). More specifically, this
|
||||
* event handler will be called *before* the event is actually deleted.
|
||||
* @param pev the event to be deleted when returning from the event handler
|
||||
*/
|
||||
virtual void onEventDeletion(BaseEvent* pev) { }
|
||||
/**
|
||||
* This method is called when an previously added event is about to be
|
||||
* triggered by the simulator-backend. More specifically, this event handler
|
||||
* will be called *before* the event is actually triggered, i.e. before the
|
||||
* corresponding coroutine is toggled.
|
||||
* @param pev the event to be triggered when returning from the event handler
|
||||
*/
|
||||
virtual void onEventTrigger(BaseEvent* pev) { }
|
||||
/* ********************************************************************
|
||||
* Simulator Controller & Access API:
|
||||
* ********************************************************************/
|
||||
/**
|
||||
* Save simulator state.
|
||||
* @param path Location to store state information
|
||||
*/
|
||||
virtual void save(const std::string& path) = 0;
|
||||
/**
|
||||
* Restore simulator state. Implicitly discards all previously
|
||||
* registered events.
|
||||
* @param path Location to previously saved state information
|
||||
*/
|
||||
virtual void restore(const std::string& path) = 0;
|
||||
/**
|
||||
* Reboot simulator.
|
||||
*/
|
||||
virtual void reboot() = 0;
|
||||
/**
|
||||
* Terminate simulator
|
||||
* @param exCode Individual exit code
|
||||
*/
|
||||
void terminate(int exCode = EXIT_SUCCESS) __attribute__ ((noreturn));
|
||||
/**
|
||||
* Check whether the interrupt should be suppressed.
|
||||
* @param interruptNum the interrupt-type id
|
||||
* @return \c true if the interrupt is suppressed, \c false oterwise
|
||||
*/
|
||||
bool isSuppressedInterrupt(unsigned interruptNum);
|
||||
/**
|
||||
* Add a Interrupt to the list of suppressed.
|
||||
* @param interruptNum the interrupt-type id
|
||||
* @return \c true if sucessfully added, \c false otherwise (already
|
||||
* existing)
|
||||
*/
|
||||
bool addSuppressedInterrupt(unsigned interruptNum);
|
||||
/**
|
||||
* Remove a Interrupt from the list of suppressed.
|
||||
* @param interruptNum the interrupt-type id
|
||||
* @return \c true if sucessfully removed, \c false otherwise (not
|
||||
* found)
|
||||
*/
|
||||
bool removeSuppressedInterrupt(unsigned interruptNum);
|
||||
/**
|
||||
* Returns the (constant) initialized register manager.
|
||||
* @return a reference to the register manager
|
||||
*/
|
||||
RegisterManager& getRegisterManager() { return (*m_Regs); }
|
||||
const RegisterManager& getRegisterManager() const { return (*m_Regs); }
|
||||
/**
|
||||
* Sets the register manager.
|
||||
* @param pReg the new register manager (or a concrete derived class of
|
||||
* RegisterManager)
|
||||
*/
|
||||
void setRegisterManager(RegisterManager* pReg) { m_Regs = pReg; }
|
||||
/**
|
||||
* Returns the (constant) initialized memory manager.
|
||||
* @return a reference to the memory manager
|
||||
*/
|
||||
MemoryManager& getMemoryManager() { return (*m_Mem); }
|
||||
const MemoryManager& getMemoryManager() const { return (*m_Mem); }
|
||||
/**
|
||||
* Sets the memory manager.
|
||||
* @param pMem a new concrete memory manager
|
||||
*/
|
||||
void setMemoryManager(MemoryManager* pMem) { m_Mem = pMem; }
|
||||
/* ********************************************************************
|
||||
* Experiment-Flow & Event Management API:
|
||||
* ********************************************************************/
|
||||
/**
|
||||
* Adds the specified experiment or plugin and creates a coroutine to
|
||||
* run it in.
|
||||
* @param flow the experiment flow object to be added
|
||||
*/
|
||||
void addFlow(ExperimentFlow* flow);
|
||||
/**
|
||||
* Removes the specified experiment or plugin and destroys its coroutine
|
||||
* and all associated events.
|
||||
* @param flow the experiment flow object to be removed
|
||||
*/
|
||||
void removeFlow(ExperimentFlow* flow);
|
||||
/**
|
||||
* Add event ev to the event management. This causes the event to be
|
||||
* active.
|
||||
* @param ev the event pointer to be added for the current flow
|
||||
* @return the id of the event used to identify the object on occurrence;
|
||||
* -1 is returned on errors
|
||||
*/
|
||||
EventId addEvent(BaseEvent* ev);
|
||||
/**
|
||||
* Removes the event with the specified id.
|
||||
* @param ev the pointer of the event-object to be removed; if \a ev is
|
||||
* equal to \c NULL all events (for all experiments) will be
|
||||
* removed
|
||||
*/
|
||||
void removeEvent(BaseEvent* ev) { m_EvList.remove(ev); }
|
||||
/**
|
||||
* Removes all previously added events for all experiments. To
|
||||
* restrict this to a specific experiment flow, pass a pointer to it.
|
||||
*/
|
||||
void clearEvents(ExperimentFlow *flow = 0) { m_EvList.remove(flow); }
|
||||
/**
|
||||
* Waits on any events which have been added to the event management. If
|
||||
* one of those events occour, waitAny() will return the id of that event.
|
||||
* @return the previously occurred event
|
||||
*
|
||||
* FIXME: Maybe this should return immediately if there are not events?
|
||||
* (additional parameter flag?)
|
||||
*/
|
||||
BaseEvent* waitAny();
|
||||
/**
|
||||
* Add event \a ev to the global buffer and wait for it (combines
|
||||
* \c addEvent() and \c waitAny()).
|
||||
* @param ev the event pointer to be added
|
||||
* @return the pointer of the occurred event (it is not guaranteed that
|
||||
* this pointer will be equal to ev)
|
||||
*
|
||||
* FIXME: Rename to make clear this returns when *any* event occurs
|
||||
*/
|
||||
BaseEvent* addEventAndWait(BaseEvent* ev);
|
||||
/**
|
||||
* Checks whether any experiment flow has events in the event-list.
|
||||
* @return \c true if there are still events, or \c false otherwise
|
||||
*/
|
||||
bool hasEvents() const { return getEventCount() > 0; }
|
||||
/**
|
||||
* Determines the number of (stored) events in the event-list which have
|
||||
* not been triggered so far.
|
||||
* @return the actual number of events
|
||||
*/
|
||||
unsigned getEventCount() const { return m_EvList.getEventCount(); }
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __SIMULATOR_CONTROLLER_HPP__
|
||||
25
src/core/sal/bochs/BochsConfig.hpp
Normal file
25
src/core/sal/bochs/BochsConfig.hpp
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* \brief Type definitions and configuration settings for
|
||||
* the Bochs simulator.
|
||||
*/
|
||||
|
||||
#ifndef __BOCHS_CONFIG_HPP__
|
||||
#define __BOCHS_CONFIG_HPP__
|
||||
|
||||
#include "bochs.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace fail {
|
||||
|
||||
typedef bx_address guest_address_t; //!< the guest memory address type
|
||||
typedef Bit8u* host_address_t; //!< the host memory address type
|
||||
#if BX_SUPPORT_X86_64
|
||||
typedef Bit64u register_data_t; //!< register data type (64 bit)
|
||||
#else
|
||||
typedef Bit32u register_data_t; //!< register data type (32 bit)
|
||||
#endif
|
||||
typedef int timer_t; //!< type of timer IDs
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __BOCHS_CONFIG_HPP__
|
||||
322
src/core/sal/bochs/BochsController.cc
Normal file
322
src/core/sal/bochs/BochsController.cc
Normal file
@ -0,0 +1,322 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "BochsController.hpp"
|
||||
#include "BochsMemory.hpp"
|
||||
#include "BochsRegister.hpp"
|
||||
#include "../Register.hpp"
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
#ifdef DANCEOS_RESTORE
|
||||
bx_bool restore_bochs_request = false;
|
||||
bx_bool save_bochs_request = false;
|
||||
std::string sr_path = "";
|
||||
#endif
|
||||
|
||||
bx_bool reboot_bochs_request = false;
|
||||
bx_bool interrupt_injection_request = false;
|
||||
int interrupt_to_fire = -1;
|
||||
|
||||
BochsController::BochsController()
|
||||
: SimulatorController(new BochsRegisterManager(), new BochsMemoryManager())
|
||||
{
|
||||
// -------------------------------------
|
||||
// Add the general purpose register:
|
||||
#if BX_SUPPORT_X86_64
|
||||
// -- 64 bit register --
|
||||
const std::string names[] = { "RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI",
|
||||
"RDI", "R8", "R9", "R10", "R11", "R12", "R13",
|
||||
"R14", "R15" };
|
||||
for(unsigned short i = 0; i < 16; i++)
|
||||
{
|
||||
BxGPReg* pReg = new BxGPReg(i, 64, &(BX_CPU(0)->gen_reg[i].rrx));
|
||||
pReg->setName(names[i]);
|
||||
m_Regs->add(pReg);
|
||||
}
|
||||
#else
|
||||
// -- 32 bit register --
|
||||
const std::string names[] = { "EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI",
|
||||
"EDI" };
|
||||
for(unsigned short i = 0; i < 8; i++)
|
||||
{
|
||||
BxGPReg* pReg = new BxGPReg(i, 32, &(BX_CPU(0)->gen_reg[i].dword.erx));
|
||||
pReg->setName(names[i]);
|
||||
m_Regs->add(pReg);
|
||||
}
|
||||
#endif // BX_SUPPORT_X86_64
|
||||
#ifdef DEBUG
|
||||
m_Regularity = 0; // disabled
|
||||
m_Counter = 0;
|
||||
m_pDest = NULL;
|
||||
#endif
|
||||
// -------------------------------------
|
||||
// Add the Program counter register:
|
||||
#if BX_SUPPORT_X86_64
|
||||
BxPCReg* pPCReg = new BxPCReg(RID_PC, 64, &(BX_CPU(0)->gen_reg[BX_64BIT_REG_RIP].rrx));
|
||||
pPCReg->setName("RIP");
|
||||
#else
|
||||
BxPCReg* pPCReg = new BxPCReg(RID_PC, 32, &(BX_CPU(0)->gen_reg[BX_32BIT_REG_EIP].dword.erx));
|
||||
pPCReg->setName("EIP");
|
||||
#endif // BX_SUPPORT_X86_64
|
||||
// -------------------------------------
|
||||
// Add the Status register (x86 cpu FLAGS):
|
||||
BxFlagsReg* pFlagReg = new BxFlagsReg(RID_FLAGS, reinterpret_cast<regdata_t*>(&(BX_CPU(0)->eflags)));
|
||||
// Note: "eflags" is (always) of type Bit32u which matches the regdata_t only in
|
||||
// case of the 32 bit version (id est !BX_SUPPORT_X86_64). Therefor we need
|
||||
// to ensure to assign only 32 bit to the Bochs internal register variable
|
||||
// (see SAL/bochs/BochsRegister.hpp, setData) if we are in 64 bit mode.
|
||||
pFlagReg->setName("FLAGS");
|
||||
m_Regs->add(pFlagReg);
|
||||
m_Regs->add(pPCReg);
|
||||
}
|
||||
|
||||
BochsController::~BochsController()
|
||||
{
|
||||
for(RegisterManager::iterator it = m_Regs->begin(); it != m_Regs->end(); it++)
|
||||
delete (*it); // free the memory, allocated in the constructor
|
||||
m_Regs->clear();
|
||||
delete m_Regs;
|
||||
delete m_Mem;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void BochsController::dbgEnableInstrPtrOutput(unsigned regularity, std::ostream* dest)
|
||||
{
|
||||
m_Regularity = regularity;
|
||||
m_pDest = dest;
|
||||
m_Counter = 0;
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
void BochsController::onInstrPtrChanged(address_t instrPtr, address_t address_space)
|
||||
{
|
||||
#if 0
|
||||
//the original code - performs magnitudes worse than
|
||||
//the code below and is responsible for most (~87 per cent)
|
||||
//of the slowdown of FailBochs
|
||||
#ifdef DEBUG
|
||||
if(m_Regularity != 0 && ++m_Counter % m_Regularity == 0)
|
||||
(*m_pDest) << "0x" << std::hex << instrPtr;
|
||||
#endif
|
||||
// Check for active breakpoint-events:
|
||||
EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end())
|
||||
{
|
||||
// FIXME: Maybe we need to improve the performance of this check.
|
||||
BPEvent* pEvBreakpt = dynamic_cast<BPEvent*>(*it);
|
||||
if(pEvBreakpt && pEvBreakpt->isMatching(instrPtr, address_space))
|
||||
{
|
||||
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
||||
it = m_EvList.makeActive(it);
|
||||
// "it" has already been set to the next element (by calling
|
||||
// makeActive()):
|
||||
continue; // -> skip iterator increment
|
||||
}
|
||||
it++;
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
#endif
|
||||
//this code is highly optimised for the average case, so it me appear a bit ugly
|
||||
bool do_fire = false;
|
||||
unsigned i = 0;
|
||||
BufferCache<BPEvent*> *buffer_cache = m_EvList.getBPBuffer();
|
||||
while(i < buffer_cache->getCount()) {
|
||||
BPEvent *pEvBreakpt = buffer_cache->get(i);
|
||||
if(pEvBreakpt->isMatching(instrPtr, address_space)) {
|
||||
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
||||
|
||||
//transition to STL: find the element we are working on in the Event List
|
||||
EventList::iterator it = std::find(m_EvList.begin(), m_EvList.end(), pEvBreakpt);
|
||||
it = m_EvList.makeActive(it);
|
||||
//find out how much elements need to be skipped to get in sync again
|
||||
//(should be one or none, the loop is just to make sure)
|
||||
for(unsigned j = i; j < buffer_cache->getCount(); j++) {
|
||||
if(buffer_cache->get(j) == (*it)) {
|
||||
i = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// we now know we need to fire the active events - usually we do not have to
|
||||
do_fire = true;
|
||||
// "i" has already been set to the next element (by calling
|
||||
// makeActive()):
|
||||
continue; // -> skip loop increment
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if(do_fire)
|
||||
m_EvList.fireActiveEvents();
|
||||
// Note: SimulatorController::onBreakpointEvent will not be invoked in this
|
||||
// implementation.
|
||||
}
|
||||
|
||||
void BochsController::onIOPortEvent(unsigned char data, unsigned port, bool out) {
|
||||
// Check for active breakpoint-events:
|
||||
EventList::iterator it = m_EvList.begin();
|
||||
while(it != m_EvList.end())
|
||||
{
|
||||
// FIXME: Maybe we need to improve the performance of this check.
|
||||
IOPortEvent* pIOPt = dynamic_cast<IOPortEvent*>(*it);
|
||||
if(pIOPt && pIOPt->isMatching(port, out))
|
||||
{
|
||||
pIOPt->setData(data);
|
||||
it = m_EvList.makeActive(it);
|
||||
// "it" has already been set to the next element (by calling
|
||||
// makeActive()):
|
||||
continue; // -> skip iterator increment
|
||||
}
|
||||
it++;
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
// Note: SimulatorController::onBreakpointEvent will not be invoked in this
|
||||
// implementation.
|
||||
}
|
||||
|
||||
void BochsController::save(const std::string& path)
|
||||
{
|
||||
int stat;
|
||||
|
||||
stat = mkdir(path.c_str(), 0777);
|
||||
if(!(stat == 0 || errno == EEXIST))
|
||||
std::cout << "[FAIL] Can not create target-directory to save!" << std::endl;
|
||||
// TODO: (Non-)Verbose-Mode? Log-level? Maybe better: use return value to indicate failure?
|
||||
|
||||
save_bochs_request = true;
|
||||
sr_path = path;
|
||||
m_CurrFlow = m_Flows.getCurrent();
|
||||
m_Flows.resume();
|
||||
}
|
||||
|
||||
void BochsController::saveDone()
|
||||
{
|
||||
save_bochs_request = false;
|
||||
m_Flows.toggle(m_CurrFlow);
|
||||
}
|
||||
|
||||
void BochsController::restore(const std::string& path)
|
||||
{
|
||||
clearEvents();
|
||||
restore_bochs_request = true;
|
||||
sr_path = path;
|
||||
m_CurrFlow = m_Flows.getCurrent();
|
||||
m_Flows.resume();
|
||||
}
|
||||
|
||||
void BochsController::restoreDone()
|
||||
{
|
||||
restore_bochs_request = false;
|
||||
m_Flows.toggle(m_CurrFlow);
|
||||
}
|
||||
|
||||
void BochsController::reboot()
|
||||
{
|
||||
clearEvents();
|
||||
reboot_bochs_request = true;
|
||||
m_CurrFlow = m_Flows.getCurrent();
|
||||
m_Flows.resume();
|
||||
}
|
||||
|
||||
void BochsController::rebootDone()
|
||||
{
|
||||
reboot_bochs_request = false;
|
||||
m_Flows.toggle(m_CurrFlow);
|
||||
}
|
||||
|
||||
void BochsController::fireInterrupt(unsigned irq)
|
||||
{
|
||||
interrupt_injection_request = true;
|
||||
interrupt_to_fire = irq;
|
||||
m_CurrFlow = m_Flows.getCurrent();
|
||||
m_Flows.resume();
|
||||
}
|
||||
|
||||
void BochsController::fireInterruptDone()
|
||||
{
|
||||
interrupt_injection_request = false;
|
||||
m_Flows.toggle(m_CurrFlow);
|
||||
}
|
||||
|
||||
void BochsController::m_onTimerTrigger(void* thisPtr)
|
||||
{
|
||||
// FIXME: The timer logic can be modified to use only one timer in Bochs.
|
||||
// (For now, this suffices.)
|
||||
TimerEvent* pTmEv = static_cast<TimerEvent*>(thisPtr);
|
||||
// Check for a matching TimerEvent. (In fact, we are only
|
||||
// interessted in the iterator pointing at pTmEv.):
|
||||
EventList::iterator it = std::find(simulator.m_EvList.begin(),
|
||||
simulator.m_EvList.end(), pTmEv);
|
||||
// TODO: This has O(|m_EvList|) time complexity. We can further improve this
|
||||
// by creating a method such that makeActive(pTmEv) works as well,
|
||||
// reducing the time complexity to O(1).
|
||||
simulator.m_EvList.makeActive(it);
|
||||
simulator.m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
timer_id_t BochsController::m_registerTimer(TimerEvent* pev)
|
||||
{
|
||||
assert(pev != NULL);
|
||||
return static_cast<timer_id_t>(
|
||||
bx_pc_system.register_timer(pev, m_onTimerTrigger, pev->getTimeout(), !pev->getOnceFlag(),
|
||||
1/*start immediately*/, "Fail*: BochsController"/*name*/));
|
||||
}
|
||||
|
||||
bool BochsController::m_unregisterTimer(TimerEvent* pev)
|
||||
{
|
||||
assert(pev != NULL);
|
||||
bx_pc_system.deactivate_timer(static_cast<unsigned>(pev->getId()));
|
||||
return bx_pc_system.unregisterTimer(static_cast<unsigned>(pev->getId()));
|
||||
}
|
||||
|
||||
bool BochsController::onEventAddition(BaseEvent* pev)
|
||||
{
|
||||
TimerEvent* tmev;
|
||||
// Register the timer event in the Bochs simulator:
|
||||
if ((tmev = dynamic_cast<TimerEvent*>(pev)) != NULL) {
|
||||
tmev->setId(m_registerTimer(tmev));
|
||||
if(tmev->getId() == -1)
|
||||
return false; // unable to register the timer (error in Bochs' function call)
|
||||
}
|
||||
// Note: Maybe more stuff to do here for other event types.
|
||||
return true;
|
||||
}
|
||||
|
||||
void BochsController::onEventDeletion(BaseEvent* pev)
|
||||
{
|
||||
TimerEvent* tmev;
|
||||
// Unregister the time event:
|
||||
if ((tmev = dynamic_cast<TimerEvent*>(pev)) != NULL) {
|
||||
m_unregisterTimer(tmev);
|
||||
}
|
||||
// Note: Maybe more stuff to do here for other event types.
|
||||
}
|
||||
|
||||
void BochsController::onEventTrigger(BaseEvent* pev)
|
||||
{
|
||||
TimerEvent* tmev;
|
||||
// Unregister the time event, if once-flag is true:
|
||||
if ((tmev = dynamic_cast<TimerEvent*>(pev)) != NULL) {
|
||||
if (tmev->getOnceFlag()) // deregister the timer (timer = single timeout)
|
||||
m_unregisterTimer(tmev);
|
||||
else // re-add the event (repetitive timer), tunneling the onEventAddition-handler
|
||||
m_EvList.add(tmev, tmev->getParent());
|
||||
}
|
||||
// Note: Maybe more stuff to do here for other event types.
|
||||
}
|
||||
|
||||
const std::string& BochsController::getMnemonic() const
|
||||
{
|
||||
static std::string str;
|
||||
bxICacheEntry_c* pEntry = BX_CPU(0)->getICacheEntry();
|
||||
assert(pEntry != NULL && "FATAL ERROR: Bochs internal function returned NULL (not expected)!");
|
||||
bxInstruction_c* pInstr = pEntry->i;
|
||||
assert(pInstr != NULL && "FATAL ERROR: Bochs internal member was NULL (not expected)!");
|
||||
const char* pszName = get_bx_opcode_name(pInstr->getIaOpcode());
|
||||
if (pszName != NULL)
|
||||
str = pszName;
|
||||
else
|
||||
str.clear();
|
||||
return str;
|
||||
}
|
||||
|
||||
} // end-of-namespace: fail
|
||||
181
src/core/sal/bochs/BochsController.hpp
Normal file
181
src/core/sal/bochs/BochsController.hpp
Normal file
@ -0,0 +1,181 @@
|
||||
#ifndef __BOCHS_CONTROLLER_HPP__
|
||||
#define __BOCHS_CONTROLLER_HPP__
|
||||
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <string.h>
|
||||
|
||||
#include "FailBochsGlobals.hpp"
|
||||
|
||||
#include "../SimulatorController.hpp"
|
||||
#include "../Event.hpp"
|
||||
|
||||
#include "bochs.h"
|
||||
#include "cpu/cpu.h"
|
||||
#include "config.h"
|
||||
#include "iodev/iodev.h"
|
||||
#include "pc_system.h"
|
||||
|
||||
namespace fail {
|
||||
|
||||
class ExperimentFlow;
|
||||
|
||||
/**
|
||||
* \class BochsController
|
||||
* Bochs-specific implementation of a SimulatorController.
|
||||
*/
|
||||
class BochsController : public SimulatorController {
|
||||
private:
|
||||
ExperimentFlow* m_CurrFlow; //!< Stores the current flow for save/restore-operations
|
||||
#ifdef DEBUG
|
||||
unsigned m_Regularity; //! regularity of instruction ptr output
|
||||
unsigned m_Counter; //! current instr-ptr counter
|
||||
std::ostream* m_pDest; //! debug output object (defaults to \c std::cout)
|
||||
#endif
|
||||
/**
|
||||
* Static internal event handler for TimerEvents. This static function is
|
||||
* called when a previously registered (Bochs) timer triggers. This function
|
||||
* searches for the provided TimerEvent object within the EventList and
|
||||
* fires such an event by calling \c fireActiveEvents().
|
||||
* @param thisPtr a pointer to the TimerEvent-object triggered
|
||||
*
|
||||
* FIXME: Due to Bochs internal timer and ips-configuration related stuff,
|
||||
* the simulator sometimes panics with "keyboard error:21" (see line
|
||||
* 1777 in bios/rombios.c, function keyboard_init()) if a TimerEvent
|
||||
* is added *before* the bios has been loaded and initialized. To
|
||||
* reproduce this error, try adding a TimerEvent as the initial step
|
||||
* in your experiment code and wait for it (addEventAndWait()).
|
||||
*/
|
||||
static void m_onTimerTrigger(void *thisPtr);
|
||||
/**
|
||||
* Registers a timer in the Bochs simulator. This timer fires \a TimerEvents
|
||||
* to inform the corresponding experiment-flow. Note that the number of timers
|
||||
* (in Bochs) is limited to \c BX_MAX_TIMERS (defaults to 64 in v2.4.6).
|
||||
* @param pev a pointer to the (experiment flow-) allocated TimerEvent object,
|
||||
* providing all required information to start the time, e.g. the
|
||||
* timeout value.
|
||||
* @return \c The unique id of the timer recently created. This id is carried
|
||||
* along with the TimerEvent, @see getId(). On error, -1 is returned
|
||||
* (e.g. because a timer with the same id is already existing)
|
||||
*/
|
||||
timer_id_t m_registerTimer(TimerEvent* pev);
|
||||
/**
|
||||
* Deletes a timer. No further events will be triggered by the timer.
|
||||
* @param pev a pointer to the TimerEvent-object to be removed
|
||||
* @return \c true if the timer with \a pev->getId() has been removed
|
||||
* successfully, \c false otherwise
|
||||
*/
|
||||
bool m_unregisterTimer(TimerEvent* pev);
|
||||
public:
|
||||
// Initialize the controller.
|
||||
BochsController();
|
||||
~BochsController();
|
||||
/* ********************************************************************
|
||||
* Standard Event Handler API:
|
||||
* ********************************************************************/
|
||||
/**
|
||||
* Instruction pointer modification handler. This method is called (from
|
||||
* the Breakpoints aspect) every time when the Bochs-internal IP changes.
|
||||
* @param instrPtr the new instruction pointer
|
||||
* @param address_space the address space the CPU is currently in
|
||||
*/
|
||||
void onInstrPtrChanged(address_t instrPtr, address_t address_space);
|
||||
/**
|
||||
* I/O port communication handler. This method is called (from
|
||||
* the IOPortCom aspect) every time when Bochs performs a port I/O operation.
|
||||
* @param data the data transmitted
|
||||
* @param port the port it was transmitted on
|
||||
* @param out true if the I/O traffic has been outbound, false otherwise
|
||||
*/
|
||||
void onIOPortEvent(unsigned char data, unsigned port, bool out);
|
||||
/**
|
||||
* This method is called when an experiment flow adds a new event by
|
||||
* calling \c simulator.addEvent(pev) or \c simulator.addEventAndWait(pev).
|
||||
* More specifically, the event will be added to the event-list first
|
||||
* (buffer-list, to be precise) and then this event handler is called.
|
||||
* @param pev the event which has been added
|
||||
* @return You should return \c true to continue and \c false to prevent
|
||||
* the addition of the event \a pev.
|
||||
*/
|
||||
bool onEventAddition(BaseEvent* pev);
|
||||
/**
|
||||
* This method is called when an experiment flow removes an event from
|
||||
* the event-management by calling \c removeEvent(prev), \c clearEvents()
|
||||
* or by deleting a complete flow (\c removeFlow). More specifically,
|
||||
* this event handler will be called *before* the event is actually deleted.
|
||||
* @param pev the event to be deleted when returning from the event handler
|
||||
*/
|
||||
void onEventDeletion(BaseEvent* pev);
|
||||
/**
|
||||
* This method is called when an previously added event is about to be
|
||||
* triggered by the simulator-backend. More specifically, this event handler
|
||||
* will be called *before* the event is actually triggered, i.e. before the
|
||||
* corresponding coroutine is toggled.
|
||||
* @param pev the event to be triggered when returning from the event handler
|
||||
*/
|
||||
void onEventTrigger(BaseEvent* pev);
|
||||
/* ********************************************************************
|
||||
* Simulator Controller & Access API:
|
||||
* ********************************************************************/
|
||||
/**
|
||||
* Save simulator state.
|
||||
* @param path Location to store state information
|
||||
*/
|
||||
void save(const std::string& path);
|
||||
/**
|
||||
* Save finished: Callback from Simulator
|
||||
*/
|
||||
void saveDone();
|
||||
/**
|
||||
* Restore simulator state. Clears all Events.
|
||||
* @param path Location to previously saved state information
|
||||
*/
|
||||
void restore(const std::string& path);
|
||||
/**
|
||||
* Restore finished: Callback from Simulator
|
||||
*/
|
||||
void restoreDone();
|
||||
/**
|
||||
* Reboot simulator. Clears all Events.
|
||||
*/
|
||||
void reboot();
|
||||
/**
|
||||
* Reboot finished: Callback from Simulator
|
||||
*/
|
||||
void rebootDone();
|
||||
/**
|
||||
* Fire an interrupt.
|
||||
* @param irq Interrupt to be fired
|
||||
*/
|
||||
void fireInterrupt(unsigned irq);
|
||||
/**
|
||||
* Fire done: Callback from Simulator
|
||||
*/
|
||||
void fireInterruptDone();
|
||||
/* ********************************************************************
|
||||
* BochsController-specific (not implemented in SimulatorController!):
|
||||
* ********************************************************************/
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* Enables instruction pointer debugging output.
|
||||
* @param regularity the output regularity; 1 to display every
|
||||
* instruction pointer, 0 to disable
|
||||
* @param dest specifies the output destition; defaults to \c std::cout
|
||||
*/
|
||||
void dbgEnableInstrPtrOutput(unsigned regularity, std::ostream* dest = &std::cout);
|
||||
#endif
|
||||
/**
|
||||
* Retrieves the textual description (mnemonic) for the current
|
||||
* instruction. The format of the returned string is Bochs-specific.
|
||||
* @return the mnemonic of the current instruction whose address
|
||||
* is given by \c Register::getInstructionPointer(). On errors,
|
||||
* the returned string is empty
|
||||
*/
|
||||
const std::string& getMnemonic() const;
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __BOCHS_CONTROLLER_HPP__
|
||||
15
src/core/sal/bochs/BochsHelpers.hpp
Normal file
15
src/core/sal/bochs/BochsHelpers.hpp
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef __BOCHS_HELPERS_HPP__
|
||||
#define __BOCHS_HELPERS_HPP__
|
||||
|
||||
#include "cpu/cpu.h"
|
||||
|
||||
static inline BX_CPU_C *getCPU(BX_CPU_C *that)
|
||||
{
|
||||
#if BX_USE_CPU_SMF == 0
|
||||
return that;
|
||||
#else
|
||||
return BX_CPU_THIS;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // __BOCHS_HELPERS_HPP__
|
||||
110
src/core/sal/bochs/BochsMemory.hpp
Normal file
110
src/core/sal/bochs/BochsMemory.hpp
Normal file
@ -0,0 +1,110 @@
|
||||
#ifndef __BOCHS_MEMORY_HPP__
|
||||
#define __BOCHS_MEMORY_HPP__
|
||||
|
||||
#include "../Memory.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class BochsMemoryManager
|
||||
* Represents a concrete implemenation of the abstract
|
||||
* MemoryManager to provide access to Bochs' memory pool.
|
||||
*/
|
||||
class BochsMemoryManager : public MemoryManager {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new MemoryManager object and initializes
|
||||
* it's attributes appropriately.
|
||||
*/
|
||||
BochsMemoryManager() : MemoryManager() { }
|
||||
/**
|
||||
* Retrieves the size of the available simulated memory.
|
||||
* @return the size of the memory pool in bytes
|
||||
*/
|
||||
size_t getPoolSize() const { return static_cast<size_t>(BX_MEM(0)->get_memory_len()); }
|
||||
/**
|
||||
* Retrieves the starting address of the host memory. This is the
|
||||
* first valid address in memory.
|
||||
* @return the starting address
|
||||
*/
|
||||
host_address_t getStartAddr() const { return 0; }
|
||||
/**
|
||||
* Retrieves the byte at address \a addr in the memory.
|
||||
* @param addr The guest address where the byte is located.
|
||||
* The address is expected to be valid.
|
||||
* @return the byte at \a addr
|
||||
*/
|
||||
byte_t getByte(guest_address_t addr)
|
||||
{
|
||||
host_address_t haddr = guestToHost(addr);
|
||||
assert(haddr != (host_address_t)ADDR_INV && "FATAL ERROR: Invalid guest address provided!");
|
||||
return static_cast<byte_t>(*reinterpret_cast<Bit8u*>(haddr));
|
||||
}
|
||||
/**
|
||||
* Retrieves \a cnt bytes at address \a addr from the memory.
|
||||
* @param addr The guest address where the bytes are located.
|
||||
* The address is expected to be valid.
|
||||
* @param cnt The number of bytes to be retrieved. \a addr + \a cnt
|
||||
* is expected to not exceed the memory limit.
|
||||
* @param dest Pointer to destination buffer to copy the data to.
|
||||
*/
|
||||
void getBytes(guest_address_t addr, size_t cnt, void *dest)
|
||||
{
|
||||
char *d = static_cast<char *>(dest);
|
||||
for (size_t i = 0; i < cnt; ++i)
|
||||
d[i] = getByte(addr + i);
|
||||
}
|
||||
/**
|
||||
* Writes the byte \a data to memory.
|
||||
* @param addr The guest address to write.
|
||||
* The address is expected to be valid.
|
||||
* @param data The new byte to write
|
||||
*/
|
||||
void setByte(guest_address_t addr, byte_t data)
|
||||
{
|
||||
host_address_t haddr = guestToHost(addr);
|
||||
assert(haddr != (host_address_t)ADDR_INV &&
|
||||
"FATAL ERROR: Invalid guest address provided!");
|
||||
*reinterpret_cast<Bit8u*>(haddr) = data;
|
||||
}
|
||||
/**
|
||||
* Copies data to memory.
|
||||
* @param addr The guest address to write.
|
||||
* The address is expected to be valid.
|
||||
* @param cnt The number of bytes to be retrieved. \a addr + \a cnt
|
||||
* is expected to not exceed the memory limit.
|
||||
* @param src Pointer to data to be copied.
|
||||
*/
|
||||
void setBytes(guest_address_t addr, size_t cnt, void const *src)
|
||||
{
|
||||
char const *s = static_cast<char const *>(src);
|
||||
for (size_t i = 0; i < cnt; ++i)
|
||||
setByte(addr + i, s[i]);
|
||||
}
|
||||
/**
|
||||
* Transforms the guest address \a addr to a host address.
|
||||
* @param addr The (logical) guest address to be transformed
|
||||
* @return the transformed (host) address or \c ADDR_INV on errors
|
||||
*/
|
||||
host_address_t guestToHost(guest_address_t addr)
|
||||
{
|
||||
const unsigned SEGMENT_SELECTOR_IDX = 2; // always the code segment
|
||||
const bx_address logicalAddr = static_cast<bx_address>(addr); // offset within the segment
|
||||
// Get the linear address:
|
||||
Bit32u linearAddr = BX_CPU(0)->get_laddr32(SEGMENT_SELECTOR_IDX/*seg*/, logicalAddr/*offset*/);
|
||||
// Map the linear address to the physical address:
|
||||
bx_phy_address physicalAddr;
|
||||
bx_bool fValid = BX_CPU(0)->dbg_xlate_linear2phy(linearAddr, (bx_phy_address*)&physicalAddr);
|
||||
// Determine the *host* address of the physical address:
|
||||
Bit8u* hostAddr = BX_MEM(0)->getHostMemAddr(BX_CPU(0), physicalAddr, BX_READ);
|
||||
// Now, hostAddr contains the "final" address
|
||||
if (!fValid)
|
||||
return ((host_address_t)ADDR_INV); // error
|
||||
else
|
||||
return (reinterpret_cast<host_address_t>(hostAddr)); // okay
|
||||
}
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __BOCHS_MEMORY_HPP__
|
||||
53
src/core/sal/bochs/BochsNonVerbose.ah
Normal file
53
src/core/sal/bochs/BochsNonVerbose.ah
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef __BOCHS_NON_VERBOSE_AH__
|
||||
#define __BOCHS_NON_VERBOSE_AH__
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#ifdef CONFIG_BOCHS_NON_VERBOSE
|
||||
|
||||
#include "bochs.h"
|
||||
|
||||
/*
|
||||
// Doesn't work because AspectC++ doesn't deal properly with variadic parameter
|
||||
// lists:
|
||||
aspect BochsNonVerbose {
|
||||
// Needed to suppress Bochs output *before* a state restore finished
|
||||
// FIXME: ac++ segfaults if we use call() instead of execution().
|
||||
advice execution("% logfunctions::debug(...)")
|
||||
|| execution("% logfunctions::info(...)")
|
||||
|| execution("% logfunctions::pass(...)")
|
||||
|| execution("% logfunctions::error(...)")
|
||||
: around () { }
|
||||
};
|
||||
*/
|
||||
|
||||
aspect BochsNonVerbose {
|
||||
// needed to suppress Bochs output *before* a state restore finished
|
||||
advice call("int logfunctions::get_default_action(int)")
|
||||
: around ()
|
||||
{
|
||||
int action;
|
||||
switch (*(tjp->arg<0>())) {
|
||||
case LOGLEV_DEBUG:
|
||||
case LOGLEV_PASS:
|
||||
case LOGLEV_INFO:
|
||||
action = ACT_IGNORE;
|
||||
break;
|
||||
case LOGLEV_ERROR:
|
||||
action = ACT_REPORT;
|
||||
break;
|
||||
case LOGLEV_PANIC:
|
||||
default:
|
||||
action = ACT_FATAL;
|
||||
}
|
||||
*(tjp->result()) = action;
|
||||
}
|
||||
|
||||
// No credits header
|
||||
advice call("void bx_print_header()")
|
||||
: around () { }
|
||||
};
|
||||
|
||||
#endif // CONFIG_BOCHS_NON_VERBOSE
|
||||
|
||||
#endif // __BOCHS_NON_VERBOSE_AH__
|
||||
238
src/core/sal/bochs/BochsRegister.hpp
Normal file
238
src/core/sal/bochs/BochsRegister.hpp
Normal file
@ -0,0 +1,238 @@
|
||||
#ifndef __BOCHS_REGISTER_HPP__
|
||||
#define __BOCHS_REGISTER_HPP__
|
||||
|
||||
#include "../Register.hpp"
|
||||
|
||||
#include "bochs.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class BochsRegister
|
||||
* Bochs-specific implementation of x86 registers.
|
||||
*/
|
||||
class BochsRegister : public Register {
|
||||
protected:
|
||||
regdata_t* m_pData;
|
||||
public:
|
||||
/**
|
||||
* Constructs a new register object.
|
||||
* @param id the global unique id
|
||||
* @param width width of the register (8, 16, 32 or 64 bit should
|
||||
* suffice)
|
||||
* @param link pointer to bochs interal register memory
|
||||
* @param t type of the register
|
||||
*/
|
||||
BochsRegister(unsigned int id, regwidth_t width, regdata_t* link, RegisterType t)
|
||||
: Register(id, t, width), m_pData(link) { }
|
||||
/**
|
||||
* Retrieves the data of the register.
|
||||
* @return the current register data
|
||||
*/
|
||||
regdata_t getData() { return (*m_pData); }
|
||||
/**
|
||||
* Sets the content of the register.
|
||||
* @param data the new register data to be written
|
||||
*/
|
||||
void setData(regdata_t data) { *m_pData = data; }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BxGPReg
|
||||
* Bochs-specific implementation of x86 general purpose (GP) registers.
|
||||
*/
|
||||
class BxGPReg : public BochsRegister {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new general purpose register.
|
||||
* @param id the global unique id
|
||||
* @param width width of the register (8, 16, 32 or 64 bit should
|
||||
* suffice)
|
||||
* @param link pointer to bochs interal register memory
|
||||
*/
|
||||
BxGPReg(unsigned int id, regwidth_t width, regdata_t* link)
|
||||
: BochsRegister(id, width, link, RT_GP) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* \enum GPRegisterId
|
||||
* Symbolic identifier to access Bochs' general purpose register
|
||||
* (within the corresponding GP set), e.g.
|
||||
* \code
|
||||
* // Print %eax register data:
|
||||
* BochsController bc(...);
|
||||
* cout << bc.getRegisterManager().getSetOfType(RT_GP)
|
||||
* .getRegister(RID_EAX)->getData();
|
||||
* \endcode
|
||||
*/
|
||||
enum GPRegisterId {
|
||||
#if BX_SUPPORT_X86_64 // 64 bit register id's:
|
||||
RID_RAX = 0, RID_RCX, RID_RDX, RID_RBX, RID_RSP, RID_RBP, RID_RSI, RID_RDI,
|
||||
RID_R8, RID_R9, RID_R10, RID_R11, RID_R12, RID_R13, RID_R14, RID_R15,
|
||||
#else // 32 bit register id's:
|
||||
RID_EAX = 0, RID_ECX, RID_EDX, RID_EBX, RID_ESP, RID_EBP, RID_ESI, RID_EDI,
|
||||
#endif
|
||||
RID_CAX = 0, RID_CCX, RID_CDX, RID_CBX, RID_CSP, RID_CBP, RID_CSI, RID_CDI,
|
||||
RID_LAST_GP_ID
|
||||
};
|
||||
|
||||
/**
|
||||
* \enum PCRegisterId
|
||||
* Symbolic identifier to access Bochs' program counter register.
|
||||
*/
|
||||
enum PCRegisterId { RID_PC = RID_LAST_GP_ID, RID_LAST_PC_ID };
|
||||
|
||||
/**
|
||||
* \enum FlagsRegisterId
|
||||
* Symbolic identifier to access Bochs' flags register.
|
||||
*/
|
||||
enum FlagsRegisterId { RID_FLAGS = RID_LAST_PC_ID };
|
||||
|
||||
/**
|
||||
* \class BxPCReg
|
||||
* Bochs-specific implementation of the x86 program counter register.
|
||||
*/
|
||||
class BxPCReg : public BochsRegister {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new program counter register.
|
||||
* @param id the global unique id
|
||||
* @param width width of the register (8, 16, 32 or 64 bit should
|
||||
* suffice)
|
||||
* @param link pointer to bochs internal register memory
|
||||
*/
|
||||
BxPCReg(unsigned int id, regwidth_t width, regdata_t* link)
|
||||
: BochsRegister(id, width, link, RT_PC) { }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BxFlagsReg
|
||||
* Bochs-specific implementation of the FLAGS status register.
|
||||
*/
|
||||
class BxFlagsReg : public BochsRegister {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new FLAGS status register. The refenced FLAGS are
|
||||
* allocated as follows:
|
||||
* --------------------------------------------------
|
||||
* 31|30|29|28| 27|26|25|24| 23|22|21|20| 19|18|17|16
|
||||
* ==|==|=====| ==|==|==|==| ==|==|==|==| ==|==|==|==
|
||||
* 0| 0| 0| 0| 0| 0| 0| 0| 0| 0|ID|VP| VF|AC|VM|RF
|
||||
*
|
||||
* 15|14|13|12| 11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0
|
||||
* ==|==|=====| ==|==|==|==| ==|==|==|==| ==|==|==|==
|
||||
* 0|NT| IOPL| OF|DF|IF|TF| SF|ZF| 0|AF| 0|PF| 1|CF
|
||||
* --------------------------------------------------
|
||||
* @param id the global unique id
|
||||
* @param link pointer to bochs internal register memory
|
||||
*/
|
||||
BxFlagsReg(unsigned int id, regdata_t* link)
|
||||
: BochsRegister(id, 32, link, RT_ST) { }
|
||||
|
||||
/**
|
||||
* Returns \c true if the corresponding flag is set, or \c false
|
||||
* otherwise.
|
||||
*/
|
||||
bool getCarryFlag() const { return (BX_CPU(0)->get_CF()); }
|
||||
bool getParityFlag() const { return (BX_CPU(0)->get_PF()); }
|
||||
bool getZeroFlag() const { return (BX_CPU(0)->get_ZF()); }
|
||||
bool getSignFlag() const { return (BX_CPU(0)->get_SF()); }
|
||||
bool getOverflowFlag() const { return (BX_CPU(0)->get_OF()); }
|
||||
|
||||
bool getTrapFlag() const { return (BX_CPU(0)->get_TF()); }
|
||||
bool getInterruptFlag() const { return (BX_CPU(0)->get_IF()); }
|
||||
bool getDirectionFlag() const { return (BX_CPU(0)->get_DF()); }
|
||||
unsigned getIOPrivilegeLevel() const { return (BX_CPU(0)->get_IOPL()); }
|
||||
bool getNestedTaskFlag() const { return (BX_CPU(0)->get_NT()); }
|
||||
bool getResumeFlag() const { return (BX_CPU(0)->get_RF()); }
|
||||
bool getVMFlag() const { return (BX_CPU(0)->get_VM()); }
|
||||
bool getAlignmentCheckFlag() const { return (BX_CPU(0)->get_AC()); }
|
||||
bool getVInterruptFlag() const { return (BX_CPU(0)->get_VIF()); }
|
||||
bool getVInterruptPendingFlag() const { return (BX_CPU(0)->get_VIP()); }
|
||||
bool getIdentificationFlag() const { return (BX_CPU(0)->get_ID()); }
|
||||
|
||||
/**
|
||||
* Sets/resets various status FLAGS.
|
||||
*/
|
||||
void setCarryFlag(bool bit) { BX_CPU(0)->set_CF(bit); }
|
||||
void setParityFlag(bool bit) { BX_CPU(0)->set_PF(bit); }
|
||||
void setZeroFlag(bool bit) { BX_CPU(0)->set_ZF(bit); }
|
||||
void setSignFlag(bool bit) { BX_CPU(0)->set_SF(bit); }
|
||||
void setOverflowFlag(bool bit) { BX_CPU(0)->set_OF(bit); }
|
||||
|
||||
void setTrapFlag(bool bit) { BX_CPU(0)->set_TF(bit); }
|
||||
void setInterruptFlag(bool bit) { BX_CPU(0)->set_IF(bit); }
|
||||
void setDirectionFlag(bool bit) { BX_CPU(0)->set_DF(bit); }
|
||||
void setIOPrivilegeLevel(unsigned lvl) { BX_CPU(0)->set_IOPL(lvl); }
|
||||
void setNestedTaskFlag(bool bit) { BX_CPU(0)->set_NT(bit); }
|
||||
void setResumeFlag(bool bit) { BX_CPU(0)->set_RF(bit); }
|
||||
void setVMFlag(bool bit) { BX_CPU(0)->set_VM(bit); }
|
||||
void setAlignmentCheckFlag(bool bit) { BX_CPU(0)->set_AC(bit); }
|
||||
void setVInterruptFlag(bool bit) { BX_CPU(0)->set_VIF(bit); }
|
||||
void setVInterruptPendingFlag(bool bit) { BX_CPU(0)->set_VIP(bit); }
|
||||
void setIdentificationFlag(bool bit) { BX_CPU(0)->set_ID(bit); }
|
||||
|
||||
/**
|
||||
* Sets the content of the status register.
|
||||
* @param data the new register data to be written; note that only the
|
||||
* 32 lower bits are used (bits 32-63 are ignored in 64 bit mode)
|
||||
*/
|
||||
void setData(regdata_t data)
|
||||
{
|
||||
#ifdef BX_SUPPORT_X86_64
|
||||
// We are in 64 bit mode: Just assign the lower 32 bits!
|
||||
(*m_pData) = ((*m_pData) & 0xFFFFFFFF00000000ULL) |
|
||||
(data & 0xFFFFFFFFULL);
|
||||
#else
|
||||
*m_pData = data;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \class BochsRegister
|
||||
* Bochs-specific implementation of the RegisterManager.
|
||||
*/
|
||||
class BochsRegisterManager : public RegisterManager {
|
||||
public:
|
||||
/**
|
||||
* Returns the current instruction pointer.
|
||||
* @return the current eip
|
||||
*/
|
||||
address_t getInstructionPointer()
|
||||
{
|
||||
return (static_cast<address_t>(getSetOfType(RT_PC)->first()->getData()));
|
||||
}
|
||||
/**
|
||||
* Retruns the top address of the stack.
|
||||
* @return the starting address of the stack
|
||||
*/
|
||||
address_t getStackPointer()
|
||||
{
|
||||
#if BX_SUPPORT_X86_64
|
||||
return (static_cast<address_t>(getRegister(RID_RSP)->getData()));
|
||||
#else
|
||||
return (static_cast<address_t>(getRegister(RID_ESP)->getData()));
|
||||
#endif
|
||||
}
|
||||
/**
|
||||
* Retrieves the base ptr (holding the address of the
|
||||
* current stack frame).
|
||||
* @return the base pointer
|
||||
*/
|
||||
address_t getBasePointer()
|
||||
{
|
||||
#if BX_SUPPORT_X86_64
|
||||
return (static_cast<address_t>(getRegister(RID_RBP)->getData()));
|
||||
#else
|
||||
return (static_cast<address_t>(getRegister(RID_EBP)->getData()));
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __BOCHS_REGISTER_HPP__
|
||||
32
src/core/sal/bochs/Breakpoints.ah
Normal file
32
src/core/sal/bochs/Breakpoints.ah
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef __BREAKPOINTS_AH__
|
||||
#define __BREAKPOINTS_AH__
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#ifdef CONFIG_EVENT_BREAKPOINTS
|
||||
|
||||
#include "bochs.h"
|
||||
#include "cpu/cpu.h"
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect Breakpoints {
|
||||
pointcut cpuLoop() = "void defineCPULoopJoinPoint(...)";
|
||||
|
||||
advice execution (cpuLoop()) : after () // Event source: "instruction pointer"
|
||||
{
|
||||
// Points to the cpu class: "this" if BX_USE_CPU_SMF == 0,
|
||||
// BX_CPU(0) otherwise
|
||||
BX_CPU_C* pThis = *(tjp->arg<0>());
|
||||
// Points to the *current* bxInstruction-object
|
||||
//bxInstruction_c* pInstr = *(tjp->arg<1>());
|
||||
|
||||
// report this event to the Bochs controller:
|
||||
fail::simulator.onInstrPtrChanged(pThis->get_instruction_pointer(), pThis->cr3);
|
||||
// Note: get_bx_opcode_name(pInstr->getIaOpcode()) retrieves the mnemonics.
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_EVENT_BREAKPOINTS
|
||||
|
||||
#endif // __BREAKPOINTS_AH__
|
||||
29
src/core/sal/bochs/Credits.ah
Normal file
29
src/core/sal/bochs/Credits.ah
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef __CREDITS_AH__
|
||||
#define __CREDITS_AH__
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
aspect Credits {
|
||||
bool first;
|
||||
Credits() : first(true) {}
|
||||
|
||||
advice call ("% bx_center_print(...)")
|
||||
&& within ("void bx_print_header()")
|
||||
&& args(file, line, maxwidth)
|
||||
: around (FILE *file, const char *line, unsigned maxwidth)
|
||||
{
|
||||
if (!first) {
|
||||
tjp->proceed();
|
||||
return;
|
||||
}
|
||||
// FIXME take version from global configuration
|
||||
char buf[256] = "FailBochs 0.0.1, based on the ";
|
||||
strncat(buf, line, 128);
|
||||
first = false;
|
||||
*(tjp->arg<1>()) = buf;
|
||||
tjp->proceed();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __CREDITS_AH__
|
||||
26
src/core/sal/bochs/DisableKeyboardInterrupt.ah
Normal file
26
src/core/sal/bochs/DisableKeyboardInterrupt.ah
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef __DISABLE_KEYBOARD_INTERRUPT_AH__
|
||||
#define __DISABLE_KEYBOARD_INTERRUPT_AH__
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#ifdef CONFIG_DISABLE_KEYB_INTERRUPTS
|
||||
|
||||
#include "iodev/iodev.h"
|
||||
#include "iodev/keyboard.h"
|
||||
|
||||
aspect DisableKeyboardInterrupt {
|
||||
pointcut heyboard_interrupt() =
|
||||
"void bx_keyb_c::timer_handler(...)";
|
||||
|
||||
advice execution (heyboard_interrupt()) : around ()
|
||||
{
|
||||
bx_keyb_c *class_ptr = (bx_keyb_c*)tjp->arg<0>();
|
||||
unsigned retval;
|
||||
|
||||
retval = class_ptr->periodic(1);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_DISABLE_KEYB_INTERRUPTS
|
||||
|
||||
#endif // __DISABLE_KEYBOARD_INTERRUPT_AH__
|
||||
19
src/core/sal/bochs/DisableLogFunctions.ah
Normal file
19
src/core/sal/bochs/DisableLogFunctions.ah
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef __DISABLE_ADD_REMOVE_LOGFN_AH__
|
||||
#define __DISABLE_ADD_REMOVE_LOGFN_AH__
|
||||
|
||||
/* Hack to prevent Bochs' logfunctions list (bochs.h) to overflow if the
|
||||
* experiment restores simulator state more than ~1000 times.
|
||||
*
|
||||
* The "proper" fix would be to completely unregister all log functions before
|
||||
* restore, i.e. to destroy all objects deriving from class logfunctions. We
|
||||
* decided to simply ignore this tiny memory leak and to hack around the
|
||||
* problem by disabling iofunctions::add/remove_logfn().
|
||||
*/
|
||||
aspect DisableLogFunctions {
|
||||
pointcut add_remove_logfn() =
|
||||
"void iofunctions::add_logfn(...)" ||
|
||||
"void iofunctions::remove_logfn(...)";
|
||||
advice execution (add_remove_logfn()) : around () { }
|
||||
};
|
||||
|
||||
#endif // __DISABLE_ADD_REMOVE_LOGFN_AH__
|
||||
22
src/core/sal/bochs/FailBochsGlobals.hpp
Normal file
22
src/core/sal/bochs/FailBochsGlobals.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#ifndef __FAIL_BOCHS_GLOBALS_HPP__
|
||||
#define __FAIL_BOCHS_GLOBALS_HPP__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
namespace fail {
|
||||
|
||||
#ifdef DANCEOS_RESTORE
|
||||
extern bx_bool restore_bochs_request;
|
||||
extern bx_bool save_bochs_request;
|
||||
extern std::string sr_path;
|
||||
#endif
|
||||
|
||||
extern bx_bool reboot_bochs_request;
|
||||
extern bx_bool interrupt_injection_request;
|
||||
extern int interrupt_to_fire;
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __FAIL_BOCHS_GLOBALS_HPP__
|
||||
13
src/core/sal/bochs/FailBochsInit.ah
Normal file
13
src/core/sal/bochs/FailBochsInit.ah
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef __FAIL_BOCHS_INIT_AH__
|
||||
#define __FAIL_BOCHS_INIT_AH__
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect FailBochsInit {
|
||||
advice call("int bxmain()") : before ()
|
||||
{
|
||||
fail::simulator.startup();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __FAIL_BOCHS_INIT_AH__
|
||||
45
src/core/sal/bochs/FireInterrupt.ah
Normal file
45
src/core/sal/bochs/FireInterrupt.ah
Normal file
@ -0,0 +1,45 @@
|
||||
#ifndef __FIREINTERRUPT_AH__
|
||||
#define __FIREINTERRUPT_AH__
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#ifdef CONFIG_FIRE_INTERRUPTS
|
||||
|
||||
#include "bochs.h"
|
||||
#include "cpu/cpu.h"
|
||||
#include "iodev/iodev.h"
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect FireInterrupt {
|
||||
pointcut cpuLoop() = "void defineCPULoopJoinPoint(...)";
|
||||
|
||||
advice execution (cpuLoop()) : before ()
|
||||
{
|
||||
if (!fail::interrupt_injection_request) {
|
||||
return;
|
||||
} else {
|
||||
BX_SET_INTR(fail::interrupt_to_fire);
|
||||
DEV_pic_raise_irq(fail::interrupt_to_fire);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
aspect InterruptDone {
|
||||
pointcut interrupt_method() = "void bx_cpu_c::interrupt(...)";
|
||||
|
||||
advice execution (interrupt_method()) : before ()
|
||||
{
|
||||
if (!fail::interrupt_injection_request) {
|
||||
return;
|
||||
} else {
|
||||
if (*(tjp->arg<0>()) == 32 + fail::interrupt_to_fire) {
|
||||
DEV_pic_lower_irq(fail::interrupt_to_fire);
|
||||
fail::simulator.fireInterruptDone();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_FIRE_INTERRUPTS
|
||||
|
||||
#endif // __FIREINTERRUPT_AH__
|
||||
33
src/core/sal/bochs/GuestSysCom.ah
Normal file
33
src/core/sal/bochs/GuestSysCom.ah
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef __GUEST_SYS_COM_AH__
|
||||
#define __GUEST_SYS_COM_AH__
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#ifdef CONFIG_EVENT_GUESTSYS
|
||||
|
||||
#include "bochs.h"
|
||||
#include "cpu/cpu.h"
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
#include "BochsHelpers.hpp"
|
||||
|
||||
// Fixed "port number" for "Guest system >> Bochs" communication
|
||||
#define BOCHS_COM_PORT 0x378
|
||||
|
||||
// FIXME: This #define should be located in a config or passed within the event object...
|
||||
|
||||
aspect GuestSysCom {
|
||||
pointcut outInstructions() = "% ...::bx_cpu_c::OUT_DX%(...)";
|
||||
|
||||
advice execution (outInstructions()) : after () // Event source: "guest system"
|
||||
{
|
||||
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
|
||||
unsigned rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
|
||||
if (rDX == BOCHS_COM_PORT)
|
||||
fail::simulator.onGuestSystemEvent((char)rAL, rDX);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_EVENT_GUESTSYS
|
||||
|
||||
#endif // __GUEST_SYS_COM_AH__
|
||||
39
src/core/sal/bochs/IOPortCom.ah
Normal file
39
src/core/sal/bochs/IOPortCom.ah
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef __IOPORT_COM_AH__
|
||||
#define __IOPORT_COM_AH__
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#ifdef CONFIG_EVENT_IOPORT
|
||||
|
||||
#include "bochs.h"
|
||||
#include "cpu/cpu.h"
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
#include "BochsHelpers.hpp"
|
||||
|
||||
// TODO: ATM only capturing bytewise output (most common, I suppose)
|
||||
|
||||
aspect IOPortCom {
|
||||
pointcut outInstruction() = "% ...::bx_cpu_c::OUT_DXAL(...)";
|
||||
|
||||
advice execution (outInstruction()) : after ()
|
||||
{
|
||||
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
|
||||
unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
|
||||
fail::simulator.onIOPortEvent(rAL, rDX, true);
|
||||
}
|
||||
|
||||
pointcut inInstruction() = "% ...::bx_cpu_c::IN_ALDX(...)";
|
||||
|
||||
advice execution (inInstruction()) : after ()
|
||||
{
|
||||
unsigned rDX = getCPU(tjp->that())->gen_reg[2].word.rx; // port number
|
||||
unsigned char rAL = getCPU(tjp->that())->gen_reg[0].word.byte.rl; // data
|
||||
fail::simulator.onIOPortEvent(rAL, rDX, false);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_EVENT_IOPORT
|
||||
|
||||
#endif // __IOPORT_COM_AH__
|
||||
39
src/core/sal/bochs/Interrupt.ah
Normal file
39
src/core/sal/bochs/Interrupt.ah
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef __INTERRUPT_AH__
|
||||
#define __INTERRUPT_AH__
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#ifdef CONFIG_EVENT_INTERRUPT
|
||||
|
||||
#include "bochs.h"
|
||||
#include "cpu/cpu.h"
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect Interrupt {
|
||||
pointcut interrupt_method() = "void bx_cpu_c::interrupt(...)"; // cpu/exception.cc
|
||||
|
||||
advice execution (interrupt_method()) : before ()
|
||||
{
|
||||
// There are six different type-arguments for the interrupt-method
|
||||
// in cpu.h (lines 3867-3872):
|
||||
// - BX_EXTERNAL_INTERRUPT = 0,
|
||||
// - BX_NMI = 2,
|
||||
// - BX_HARDWARE_EXCEPTION = 3,
|
||||
// - BX_SOFTWARE_INTERRUPT = 4,
|
||||
// - BX_PRIVILEGED_SOFTWARE_INTERRUPT = 5,
|
||||
// - BX_SOFTWARE_EXCEPTION = 6
|
||||
// Only the first and the second types are relevant for this aspect.
|
||||
|
||||
unsigned vector = *(tjp->arg<0>());
|
||||
unsigned type = *(tjp->arg<1>());
|
||||
if (type == BX_EXTERNAL_INTERRUPT)
|
||||
fail::simulator.onInterruptEvent(vector, false);
|
||||
else if (type == BX_NMI)
|
||||
fail::simulator.onInterruptEvent(vector, true);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_EVENT_INTERRUPT
|
||||
|
||||
#endif // __INTERRUPT_AH__
|
||||
27
src/core/sal/bochs/InterruptSuppression.ah
Normal file
27
src/core/sal/bochs/InterruptSuppression.ah
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef __INTERRUPT_SUPPRESSION_AH__
|
||||
#define __INTERRUPT_SUPPRESSION_AH__
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#ifdef CONFIG_SUPPRESS_INTERRUPTS
|
||||
|
||||
#include "bochs.h"
|
||||
#include "cpu/cpu.h"
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect InterruptSuppression {
|
||||
pointcut interrupt_method() = "void bx_cpu_c::interrupt(...)";
|
||||
|
||||
advice execution (interrupt_method()) : around ()
|
||||
{
|
||||
unsigned vector = *(tjp->arg<0>());
|
||||
if (!fail::simulator.isSuppressedInterrupt(vector)) {
|
||||
tjp->proceed();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_SUPPRESS_INTERRUPTS
|
||||
|
||||
#endif // __INTERRUPT_SUPPRESSION_AH__
|
||||
140
src/core/sal/bochs/Jump.ah
Normal file
140
src/core/sal/bochs/Jump.ah
Normal file
@ -0,0 +1,140 @@
|
||||
#ifndef __JUMP_AH__
|
||||
#define __JUMP_AH__
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#ifdef CONFIG_EVENT_JUMP
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
#include "bochs.h"
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
// FIXME: This seems (partial) deprecated/incomplete as well...
|
||||
|
||||
aspect Jump {
|
||||
// Note: Have a look at the Bochs-Code (cpu/cpu.h) and the Intel
|
||||
// Architecture Software Developer's Manual - Instruction Set Reference
|
||||
// p. 3-329 (PDF p. 369) for further information:
|
||||
// http://www.intel.com/design/intarch/manuals/243191.htm
|
||||
|
||||
// Default conditional-jump instructions: they are evaluating the FLAGS,
|
||||
// defined in ctrl_xfer[16 | 32 | 64].cc, Postfix: 16-Bit: w, 32-Bit: d, 64-Bit: q
|
||||
pointcut defJumpInstructions() =
|
||||
"% ...::bx_cpu_c::JO_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNO_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JB_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNB_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JZ_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNZ_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JBE_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNBE_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JS_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNS_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JP_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNP_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JL_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNL_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JLE_J%(...)" ||
|
||||
"% ...::bx_cpu_c::JNLE_J%(...)";
|
||||
|
||||
// Special cases: they are evaluating the content of the CX-/ECX-/RCX-registers
|
||||
// (NOT the FLAGS):
|
||||
pointcut regJumpInstructions() =
|
||||
"% ...::bx_cpu_c::JCXZ_Jb(...)" || // ctrl_xfer16.cc
|
||||
"% ...::bx_cpu_c::JECXZ_Jb(...)" || // ctrl_xfer32.cc
|
||||
"% ...::bx_cpu_c::JRCXZ_Jb(...)"; // ctrl_xfer64.cc
|
||||
|
||||
/* TODO: Loop-Instructions needed? Example: "LOOPNE16_Jb"
|
||||
pointcut loopInstructions() = ...;
|
||||
|
||||
*/
|
||||
/* TODO: Conditional-Data-Moves needed? Example: "CMOVZ_GwEwR"
|
||||
pointcut dataMoveInstructions() = ...;
|
||||
|
||||
*/
|
||||
advice execution (defJumpInstructions()) : around()
|
||||
{
|
||||
bxInstruction_c* pInstr = *(tjp->arg<0>()); // bxInstruction_c-object
|
||||
fail::simulator.onJumpEvent(true, pInstr->getIaOpcode());
|
||||
/*
|
||||
JoinPoint::That* pThis = tjp->that();
|
||||
if(pThis == NULL)
|
||||
pThis = BX_CPU(0);
|
||||
assert(pThis != NULL && "FATAL ERROR: tjp->that() returned null ptr! (Maybe called on a static instance?)");
|
||||
// According to the Intel-Spec (p. 3-329f), the following flags are relevant:
|
||||
bool fZeroFlag = pThis->get_ZF();
|
||||
bool fCarryFlag = pThis->get_CF();
|
||||
bool fSignFlag = pThis->get_SF();
|
||||
bool fOverflowFlag = pThis->get_OF();
|
||||
bool fParityFlag = pThis->get_PF();
|
||||
|
||||
//
|
||||
// Step-1: Modify one or more of the fxxxFlag according to the error you want to inject
|
||||
// (using pThis->set_XX(new_val))
|
||||
// Step-2: Call tjp->proceed();
|
||||
// Step-3: Eventually, unwind the changes of Step-1
|
||||
//
|
||||
|
||||
// Example:
|
||||
if(g_fEnableInjection) // event fired?
|
||||
{
|
||||
g_fEnableInjection = false;
|
||||
// Obviously, this advice matches *all* jump-instructions so that it is not clear
|
||||
// which flag have to be modified in order to invert the jump. For simplification,
|
||||
// we will invert *all* flags. This avoids a few case differentiations.
|
||||
cout << ">>> Manipulating jump-destination (inverted)... \"" << tjp->signature() << "\" ";
|
||||
pThis->set_ZF(!fZeroFlag);
|
||||
pThis->set_SF(!fSignFlag);
|
||||
pThis->set_CF(!fCarryFlag);
|
||||
pThis->set_OF(!fOverflowFlag);
|
||||
pThis->set_PF(!fParityFlag);
|
||||
tjp->proceed();
|
||||
pThis->set_ZF(fZeroFlag);
|
||||
pThis->set_SF(fSignFlag);
|
||||
pThis->set_CF(fCarryFlag);
|
||||
pThis->set_OF(fOverflowFlag);
|
||||
pThis->set_PF(fParityFlag);
|
||||
cout << "finished (jump taken)!" << endl;
|
||||
}
|
||||
else
|
||||
*/
|
||||
tjp->proceed();
|
||||
}
|
||||
|
||||
advice execution (regJumpInstructions()) : around ()
|
||||
{
|
||||
bxInstruction_c* pInstr = *(tjp->arg<0>()); // bxInstruction_c-object
|
||||
fail::simulator.onJumpEvent(false, pInstr->getIaOpcode());
|
||||
/*
|
||||
JoinPoint::That* pThis = tjp->that();
|
||||
|
||||
assert(pThis != NULL && "Unexpected: tjp->that() returned null ptr! (Maybe called on a static instance?)");
|
||||
|
||||
// Note: Direct access to the registers using the bochs-macros (defined in
|
||||
// bochs.h) is not possibly due to the fact that they are using the this-ptr.
|
||||
|
||||
Bit16u CX = (Bit16u)(pThis->gen_reg[1].word.rx);
|
||||
Bit32u ECX = (Bit32u)(pThis->gen_reg[1].dword.erx);
|
||||
#if BX_SUPPORT_X86_64
|
||||
Bit64u RCX = (Bit64u)(pThis->gen_reg[1].rrx);
|
||||
#endif
|
||||
|
||||
//
|
||||
// Step-1: Modify CX, ECX or RCX according to the error you want to inject
|
||||
// Step-2: Call tjp->proceed();
|
||||
// Step-3: Eventually, unwind the changes of Step-1
|
||||
//
|
||||
*/
|
||||
tjp->proceed();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_EVENT_JUMP
|
||||
|
||||
#endif // __JUMP_AH__
|
||||
|
||||
158
src/core/sal/bochs/MemAccess.ah
Normal file
158
src/core/sal/bochs/MemAccess.ah
Normal file
@ -0,0 +1,158 @@
|
||||
#ifndef __MEM_ACCESS_AH__
|
||||
#define __MEM_ACCESS_AH__
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#if defined(CONFIG_EVENT_MEMREAD) || defined(CONFIG_EVENT_MEMWRITE)
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "bochs.h"
|
||||
#include "cpu/cpu.h"
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
#include "BochsHelpers.hpp"
|
||||
|
||||
// FIXME: We currently assume a "flat" memory model and ignore the segment
|
||||
// parameter of all memory accesses.
|
||||
// TODO: Instruction fetch?
|
||||
// TODO: Warn on uncovered memory accesses.
|
||||
|
||||
aspect MemAccess {
|
||||
fail::address_t rmw_address;
|
||||
|
||||
pointcut write_methods() =
|
||||
"% ...::bx_cpu_c::write_virtual_%(...)" && // -> access32/64.cc
|
||||
// not an actual memory access:
|
||||
!"% ...::bx_cpu_c::write_virtual_checks(...)";
|
||||
pointcut write_methods_RMW() =
|
||||
"% ...::bx_cpu_c::write_RMW_virtual_%(...)";
|
||||
pointcut write_methods_new_stack() =
|
||||
"% ...::bx_cpu_c::write_new_stack_%(...)" && // -> access32.cc
|
||||
!"% ...::bx_cpu_c::write_new_stack_%_64(...)";
|
||||
pointcut write_methods_new_stack_64() =
|
||||
"% ...::bx_cpu_c::write_new_stack_%_64(...)"; // -> access64.cc
|
||||
pointcut write_methods_system() =
|
||||
"% ...::bx_cpu_c::system_write_%(...)"; // -> access.cc
|
||||
// FIXME not covered:
|
||||
/* "% ...::bx_cpu_c::v2h_write_byte(...)"; // -> access.cc */
|
||||
|
||||
pointcut read_methods() =
|
||||
"% ...::bx_cpu_c::read_virtual_%(...)" &&
|
||||
// sizeof() doesn't work here (see next pointcut)
|
||||
!"% ...::bx_cpu_c::read_virtual_dqword_%(...)" && // -> access32/64.cc
|
||||
// not an actual memory access:
|
||||
!"% ...::bx_cpu_c::read_virtual_checks(...)";
|
||||
pointcut read_methods_dqword() =
|
||||
"% ...::bx_cpu_c::read_virtual_dqword_%(...)"; // -> access32/64.cc
|
||||
pointcut read_methods_RMW() =
|
||||
"% ...::bx_cpu_c::read_RMW_virtual_%(...)";
|
||||
pointcut read_methods_system() =
|
||||
"% ...::bx_cpu_c::system_read_%(...)"; // -> access.cc
|
||||
// FIXME not covered:
|
||||
/* "% ...::bx_cpu_c::v2h_read_byte(...)"; // -> access.cc */
|
||||
|
||||
//
|
||||
// Fire a memory-write-event each time the guest system requests
|
||||
// to write data to RAM:
|
||||
//
|
||||
// Event source: "memory write access"
|
||||
//
|
||||
#ifdef CONFIG_EVENT_MEMWRITE
|
||||
advice execution (write_methods()) : after ()
|
||||
{
|
||||
fail::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<1>()), sizeof(*(tjp->arg<2>())), true,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
}
|
||||
|
||||
advice execution (write_methods_RMW()) : after ()
|
||||
{
|
||||
fail::simulator.onMemoryAccessEvent(
|
||||
rmw_address, sizeof(*(tjp->arg<0>())), true,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
}
|
||||
|
||||
advice execution (write_methods_new_stack()) : after ()
|
||||
{
|
||||
std::cerr << "WOOOOOT write_methods_new_stack" << std::endl;
|
||||
// TODO: Log-level?
|
||||
fail::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<1>()), sizeof(*(tjp->arg<3>())), true,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
}
|
||||
|
||||
advice execution (write_methods_new_stack_64()) : after ()
|
||||
{
|
||||
std::cerr << "WOOOOOT write_methods_new_stack_64" << std::endl;
|
||||
// TODO: Log-level?
|
||||
fail::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<0>()), sizeof(*(tjp->arg<2>())), true,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
}
|
||||
|
||||
advice execution (write_methods_system()) : after ()
|
||||
{
|
||||
// We don't do anything here for now: This type of memory
|
||||
// access is used when the hardware itself needs to access
|
||||
// memory (e.g., to read vectors from the interrupt vector
|
||||
// table).
|
||||
/*
|
||||
fail::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<0>()), sizeof(*(tjp->arg<1>())), true,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Fire a memory-read-event each time the guest system requests
|
||||
// to read data in RAM:
|
||||
//
|
||||
// Event source: "memory read access"
|
||||
//
|
||||
#ifdef CONFIG_EVENT_MEMREAD
|
||||
advice execution (read_methods()) : before ()
|
||||
{
|
||||
fail::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<1>()), sizeof(*(tjp->result())), false,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
}
|
||||
|
||||
advice execution (read_methods_dqword()) : before ()
|
||||
{
|
||||
fail::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<1>()), 16, false,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
}
|
||||
#endif
|
||||
|
||||
advice execution (read_methods_RMW()) : before ()
|
||||
{
|
||||
rmw_address = *(tjp->arg<1>());
|
||||
#ifdef CONFIG_EVENT_MEMREAD
|
||||
fail::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<1>()), sizeof(*(tjp->result())), false,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EVENT_MEMREAD
|
||||
advice execution (read_methods_system()) : before ()
|
||||
{
|
||||
// We don't do anything here for now: This type of memory
|
||||
// access is used when the hardware itself needs to access
|
||||
// memory (e.g., to read vectors from the interrupt vector
|
||||
// table).
|
||||
/*
|
||||
fail::simulator.onMemoryAccessEvent(
|
||||
*(tjp->arg<0>()), sizeof(*(tjp->result())), false,
|
||||
getCPU(tjp->that())->prev_rip);
|
||||
*/
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // CONFIG_EVENT_MEMACCESS
|
||||
|
||||
#endif // __MEM_ACCESS_AH__
|
||||
29
src/core/sal/bochs/Reboot.ah
Normal file
29
src/core/sal/bochs/Reboot.ah
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef __REBOOT_AH__
|
||||
#define __REBOOT_AH__
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#ifdef CONFIG_SR_REBOOT
|
||||
|
||||
#include "bochs.h"
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect Reboot {
|
||||
pointcut cpuLoop() = "void defineCPULoopJoinPoint(...)";
|
||||
|
||||
advice execution (cpuLoop()) : after ()
|
||||
{
|
||||
if (!fail::reboot_bochs_request) {
|
||||
return;
|
||||
}
|
||||
|
||||
bx_gui_c::reset_handler();
|
||||
std::cout << "[FAIL] Reboot finished" << std::endl;
|
||||
// TODO: Log-level?
|
||||
fail::simulator.rebootDone();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_SR_REBOOT
|
||||
|
||||
#endif // __REBOOT_AH__
|
||||
27
src/core/sal/bochs/RestoreState.ah
Normal file
27
src/core/sal/bochs/RestoreState.ah
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef __RESTORE_STATE_AH__
|
||||
#define __RESTORE_STATE_AH__
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#ifdef CONFIG_SR_RESTORE
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "bochs.h"
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect RestoreState {
|
||||
pointcut restoreState() = "void bx_sr_after_restore_state()";
|
||||
|
||||
advice execution (restoreState()) : after ()
|
||||
{
|
||||
std::cout << "[FAIL] Restore finished" << std::endl;
|
||||
// TODO: Log-Level?
|
||||
fail::simulator.restoreDone();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_SR_RESTORE
|
||||
|
||||
#endif // __RESTORE_STATE_AH__
|
||||
33
src/core/sal/bochs/SaveState.ah
Normal file
33
src/core/sal/bochs/SaveState.ah
Normal file
@ -0,0 +1,33 @@
|
||||
#ifndef _SAVE_STATE_AH__
|
||||
#define _SAVE_STATE_AH__
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#ifdef CONFIG_SR_SAVE
|
||||
|
||||
#include "bochs.h"
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect SaveState {
|
||||
pointcut cpuLoop() = "void defineCPULoopJoinPoint(...)";
|
||||
|
||||
// make sure the "SaveState" aspect comes *after* the breakpoint stuff: In
|
||||
// an "after" advice this means it must get a *higher* precedence,
|
||||
// therefore it's first in the order list.
|
||||
advice execution (cpuLoop()) : order ("SaveState", "Breakpoints");
|
||||
|
||||
advice execution (cpuLoop()) : after () {
|
||||
if (!fail::save_bochs_request)
|
||||
return;
|
||||
assert(fail::sr_path.size() > 0 && "FATAL ERROR: tried to save state without valid path");
|
||||
SIM->save_state(fail::sr_path.c_str());
|
||||
std::cout << "[FAIL] Save finished" << std::endl;
|
||||
// TODO: Log-Level?
|
||||
fail::simulator.saveDone();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_SR_SAVE
|
||||
|
||||
#endif // _SAVE_STATE_AH__
|
||||
26
src/core/sal/bochs/Trap.ah
Normal file
26
src/core/sal/bochs/Trap.ah
Normal file
@ -0,0 +1,26 @@
|
||||
#ifndef __TRAP_AH__
|
||||
#define __TRAP_AH__
|
||||
|
||||
#include "config/FailConfig.hpp"
|
||||
|
||||
#ifdef CONFIG_EVENT_TRAP
|
||||
|
||||
#include "bochs.h"
|
||||
#include "cpu/cpu.h"
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect Trap {
|
||||
pointcut exception_method() = "void bx_cpu_c::exception(...)";
|
||||
|
||||
advice execution (exception_method()) : before ()
|
||||
{
|
||||
fail::simulator.onTrapEvent(*(tjp->arg<0>()));
|
||||
// TODO: There are some different types of exceptions at cpu.h (line 265-281)
|
||||
// Which kind of a trap are these types?
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CONFIG_EVENT_TRAP
|
||||
|
||||
#endif // __TRAP_AH__
|
||||
17
src/core/sal/ovp/FailOVPInit.ah
Normal file
17
src/core/sal/ovp/FailOVPInit.ah
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef __OVPINIT_AH__
|
||||
#define __OVPINIT_AH__
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "../SALInst.hpp"
|
||||
|
||||
aspect FailOVPInit {
|
||||
advice call("% ...::startSimulation(...)") : before ()
|
||||
{
|
||||
std::cout << "OVP init aspect!" << std::endl;
|
||||
// TODO: Log-Level?
|
||||
fail::simulator.startup();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __OVPINIT_AH__
|
||||
20
src/core/sal/ovp/OVPConfig.hpp
Normal file
20
src/core/sal/ovp/OVPConfig.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* \brief Type definitions and configuration settings for
|
||||
* the OVP simulator.
|
||||
*/
|
||||
|
||||
#ifndef __OVP_CONFIG_HPP__
|
||||
#define __OVP_CONFIG_HPP__
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace fail {
|
||||
|
||||
typedef uint32_t guest_address_t; //!< the guest memory address type
|
||||
typedef uint8_t* host_address_t; //!< the host memory address type
|
||||
typedef uint32_t register_data_t; //!< register data type (32 bit)
|
||||
typedef int timer_t; //!< type of timer IDs
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __OVP_CONFIG_HPP__
|
||||
131
src/core/sal/ovp/OVPController.cc
Normal file
131
src/core/sal/ovp/OVPController.cc
Normal file
@ -0,0 +1,131 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "OVPController.hpp"
|
||||
#include "OVPMemory.hpp"
|
||||
#include "OVPRegister.hpp"
|
||||
#include "ovp/OVPStatusRegister.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
OVPController::OVPController()
|
||||
: SimulatorController(new OVPRegisterManager(), new OVPMemoryManager())
|
||||
{
|
||||
// FIXME: This should be obsolete now...
|
||||
// set = new UniformRegisterSet(RT_GP);
|
||||
// setStatus = new UniformRegisterSet(RT_ST);
|
||||
// setPC = new UniformRegisterSet(RT_PC);
|
||||
}
|
||||
|
||||
OVPController::~OVPController()
|
||||
{
|
||||
// FIXME: This should be obsolete now...
|
||||
// delete set;
|
||||
// delete setStatus;
|
||||
// delete setPC;
|
||||
|
||||
// Free memory of OVPRegister objects (actually allocated in make*Register):
|
||||
for (RegisterManager::iterator it = m_Regs->begin(); it != m_Regs->end(); it++)
|
||||
delete (*it); // free the memory, allocated in the constructor
|
||||
}
|
||||
|
||||
void OVPController::makeGPRegister(int width, void *link, const string& name)
|
||||
{
|
||||
// Add general purpose register
|
||||
OVPRegister *reg = new OVPRegister(m_currentRegId++, width, link, RT_GP);
|
||||
reg->setName(name);
|
||||
m_Regs->add(reg);
|
||||
// Note: The RegisterManager (aka m_Regs) automatically assigns the
|
||||
// added registers (herein typed OVPRegister) to their matching
|
||||
// UniformRegisterSet (@see RegisterManager::add).
|
||||
}
|
||||
|
||||
|
||||
void OVPController::makeSTRegister(Register *reg, const string& name)
|
||||
{
|
||||
// Add status register
|
||||
reg->setName(name);
|
||||
cerr << "Add Status Register: " << reg << endl;
|
||||
m_Regs->add(reg);
|
||||
}
|
||||
|
||||
void OVPController::makePCRegister(int width, void *link, const string& name)
|
||||
{
|
||||
// Add general purpose register
|
||||
OVPRegister *reg = new OVPRegister(m_currentRegId++, width, link, RT_PC);
|
||||
reg->setName(name);
|
||||
m_Regs->add(reg);
|
||||
}
|
||||
|
||||
// FIXME: This should be obsolete now...
|
||||
/*
|
||||
void OVPController::finishedRegisterCreation()
|
||||
{
|
||||
m_Regs->add(*set);
|
||||
m_Regs->add(*setStatus);
|
||||
m_Regs->add(*setPC);
|
||||
}
|
||||
*/
|
||||
|
||||
void OVPController::onInstrPtrChanged(address_t instrPtr)
|
||||
{
|
||||
// make some funny outputs
|
||||
unsigned int r0 = m_Regs->getSetOfType(RT_GP)->getRegister(0)->getData();
|
||||
OVPRegisterManager *ovp_Regs = (OVPRegisterManager*) m_Regs;
|
||||
// FIXME: This cast seems to be invalid: RT_GP vs. OVPStatusRegister (= RT_ST)?
|
||||
OVPStatusRegister *rid_st = (OVPStatusRegister*) m_Regs->getSetOfType(RT_GP)->first();
|
||||
// cerr << "Addr: " << rid_st << endl;
|
||||
unsigned int st = rid_st->getData();
|
||||
|
||||
// save("/srv/scratch/sirozipp/test.txt");
|
||||
|
||||
// ovpplatform.setPC(0x123);
|
||||
|
||||
// restore("/srv/scratch/sirozipp/test.txt");
|
||||
// cerr << "instrPtr: 0x" << hex << instrPtr << " SP: 0x" << hex << m_Regs->getStackPointer() \
|
||||
// << " R0: 0x" << hex << r0 << " ST: 0x" << hex << st << endl;
|
||||
|
||||
// Check for active breakpoint-events:
|
||||
EventList::iterator it = m_EvList.begin();
|
||||
while (it != m_EvList.end()) {
|
||||
// FIXME: Performance verbessern (dazu muss entsprechend auch die Speicherung
|
||||
// in EventList(.cc|.hpp) angepasst bzw. verbessert werden).
|
||||
BPSingleEvent* pEvBreakpt = dynamic_cast<BPSingleEvent*>(*it);
|
||||
if (pEvBreakpt && (instrPtr == pEvBreakpt->getWatchInstructionPointer() ||
|
||||
pEvBreakpt->getWatchInstructionPointer() == ANY_ADDR)) {
|
||||
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
||||
it = m_EvList.makeActive(it);
|
||||
// "it" has already been set to the next element (by calling
|
||||
// makeActive()):
|
||||
continue; // -> skip iterator increment
|
||||
}
|
||||
BPRangeEvent* pEvRange = dynamic_cast<BPRangeEvent*>(*it);
|
||||
if (pEvRange && pEvRange->isMatching(instrPtr)) {
|
||||
pEvBreakpt->setTriggerInstructionPointer(instrPtr);
|
||||
it = m_EvList.makeActive(it);
|
||||
continue; // dito.
|
||||
}
|
||||
it++;
|
||||
}
|
||||
m_EvList.fireActiveEvents();
|
||||
}
|
||||
|
||||
void OVPController::save(const string& path)
|
||||
{
|
||||
// TODO!
|
||||
ovpplatform.save(path);
|
||||
}
|
||||
|
||||
void OVPController::restore(const string& path)
|
||||
{
|
||||
//TODO!
|
||||
assert(path.length() > 0 &&
|
||||
"FATAL ERROR: Tried to restore state without valid path!");
|
||||
ovpplatform.restore(path);
|
||||
}
|
||||
|
||||
void OVPController::reboot()
|
||||
{
|
||||
// TODO!
|
||||
}
|
||||
|
||||
} // end-of-namespace: fail
|
||||
72
src/core/sal/ovp/OVPController.hpp
Normal file
72
src/core/sal/ovp/OVPController.hpp
Normal file
@ -0,0 +1,72 @@
|
||||
#ifndef __OVP_CONTROLLER_HPP__
|
||||
#define __OVP_CONTROLLER_HPP__
|
||||
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include "../SimulatorController.hpp"
|
||||
#include "../Event.hpp"
|
||||
#include "../Register.hpp"
|
||||
#include "ovp/OVPPlatform.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
extern OVPPlatform ovpplatform;
|
||||
|
||||
/**
|
||||
* \class OVPController
|
||||
* OVP-specific implementation of a SimulatorController.
|
||||
*/
|
||||
class OVPController : public SimulatorController {
|
||||
private:
|
||||
// FIXME: This should be obsolete now...
|
||||
// UniformRegisterSet *set; //(RT_GP);
|
||||
// UniformRegisterSet *setStatus;//(RT_ST);
|
||||
// UniformRegisterSet *setPC;//(RT_PC);
|
||||
|
||||
// FIXME: Perhaps this should be declared as a static member:
|
||||
unsigned int m_currentRegId;
|
||||
// NOTE: Constants (such as GPRegisterId in sal/bochs/BochsRegister.hpp)
|
||||
// are much easier to read...
|
||||
public:
|
||||
/**
|
||||
* Initialize the controller.
|
||||
*/
|
||||
OVPController();
|
||||
virtual ~OVPController();
|
||||
virtual void onInstrPtrChanged(address_t instrPtr);
|
||||
/**
|
||||
* Save simulator state.
|
||||
* @param path Location to store state information
|
||||
*/
|
||||
virtual void save(const std::string& path);
|
||||
/**
|
||||
* Restore simulator state.
|
||||
* @param path Location to previously saved state information
|
||||
*/
|
||||
virtual void restore(const std::string& path);
|
||||
/**
|
||||
* Reboot simulator.
|
||||
*/
|
||||
virtual void reboot();
|
||||
/**
|
||||
* Returns the current instruction pointer.
|
||||
* @return the current eip
|
||||
*/
|
||||
void makeGPRegister(int, void*, const std::string&);
|
||||
void makeSTRegister(Register *, const std::string&);
|
||||
void makePCRegister(int, void*, const std::string&);
|
||||
/**
|
||||
* Due to empty RegisterSets are not allowed, OVP platform
|
||||
* must tell OVPController when it is finished
|
||||
*
|
||||
* FIXME: This should be obsolete now...
|
||||
*/
|
||||
// void finishedRegisterCreation();
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __OVP_CONTROLLER_HPP__
|
||||
73
src/core/sal/ovp/OVPMemory.hpp
Normal file
73
src/core/sal/ovp/OVPMemory.hpp
Normal file
@ -0,0 +1,73 @@
|
||||
#ifndef __OVP_MEMORY_HPP__
|
||||
#define __OVP_MEMORY_HPP__
|
||||
|
||||
#include "../Memory.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class OVPMemoryManager
|
||||
* Represents a concrete implemenation of the abstract
|
||||
* MemoryManager to provide access to OVP's memory pool.
|
||||
*/
|
||||
class OVPMemoryManager : public MemoryManager {
|
||||
public:
|
||||
/**
|
||||
* Constructs a new MemoryManager object and initializes
|
||||
* it's attributes appropriately.
|
||||
*/
|
||||
OVPMemoryManager() : MemoryManager() { }
|
||||
/**
|
||||
* Retrieves the size of the available simulated memory.
|
||||
* @return the size of the memory pool in bytes
|
||||
*/
|
||||
size_t getPoolSize() const { return 0; }
|
||||
/**
|
||||
* Retrieves the starting address of the host memory. This is the
|
||||
* first valid address in memory.
|
||||
* @return the starting address
|
||||
*/
|
||||
host_address_t getStartAddr() const { return 0; }
|
||||
/**
|
||||
* Retrieves the byte at address \a addr in the memory.
|
||||
* @param addr The guest address where the byte is located.
|
||||
* The address is expected to be valid.
|
||||
* @return the byte at \a addr
|
||||
*/
|
||||
byte_t getByte(guest_address_t addr) { return 0; }
|
||||
/**
|
||||
* Retrieves \a cnt bytes at address \a addr in the memory.
|
||||
* @param addr The guest address where the bytes are located.
|
||||
* The address is expected to be valid.
|
||||
* @param cnt the number of bytes to be retrieved. \a addr + \a cnt
|
||||
* is expected to not exceed the memory limit.
|
||||
* @param dest Pointer to destination buffer to copy the data to.
|
||||
*/
|
||||
void getBytes(guest_address_t addr, size_t cnt, void *dest) { }
|
||||
/**
|
||||
* Writes the byte \a data to memory.
|
||||
* @param addr The guest address to write.
|
||||
* The address is expected to be valid.
|
||||
* @param data The new byte to write
|
||||
*/
|
||||
void setByte(guest_address_t addr, byte_t data) { }
|
||||
/**
|
||||
* Copies data to memory.
|
||||
* @param addr The guest address to write.
|
||||
* The address is expected to be valid.
|
||||
* @param cnt The number of bytes to be retrieved. \a addr + \a cnt
|
||||
* is expected to not exceed the memory limit.
|
||||
* @param src Pointer to data to be copied.
|
||||
*/
|
||||
void setBytes(guest_address_t addr, size_t cnt, void const *src) { }
|
||||
/**
|
||||
* Transforms the guest address \a addr to a host address.
|
||||
* @param addr The (logical) guest address to be transformed
|
||||
* @return the transformed (host) address or \c ADDR_INV on errors
|
||||
*/
|
||||
host_address_t guestToHost(guest_address_t addr) { return 0; }
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __OVP_MEMORY_HPP__
|
||||
76
src/core/sal/ovp/OVPRegister.hpp
Normal file
76
src/core/sal/ovp/OVPRegister.hpp
Normal file
@ -0,0 +1,76 @@
|
||||
#ifndef __OVP_REGISTER_HPP__
|
||||
#define __OVP_REGISTER_HPP__
|
||||
|
||||
#include "../Register.hpp"
|
||||
#include "ovp/OVPPlatform.hpp"
|
||||
//#include "ovp/OVPStatusRegister.hpp"
|
||||
|
||||
extern OVPPlatform ovpplatform;
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class OVPRegister
|
||||
* OVP-specific implementation of x86 registers.
|
||||
*/
|
||||
class OVPRegister : public Register {
|
||||
private:
|
||||
void *reg;
|
||||
public:
|
||||
/**
|
||||
* Constructs a new register object.
|
||||
* @param id the unique id of this register (simulator specific)
|
||||
* @param width width of the register (8, 16, 32 or 64 bit should suffice)
|
||||
* @param link pointer to bochs internal register memory
|
||||
* @param t type of the register
|
||||
*/
|
||||
OVPRegister(unsigned int id, regwidth_t width, void* link, RegisterType t)
|
||||
: Register(id, t, width) { reg = link; }
|
||||
/**
|
||||
* Retrieves the data of the register.
|
||||
* @return the current register data
|
||||
*/
|
||||
regdata_t getData() { return ovpplatform.getRegisterData(reg); }
|
||||
/**
|
||||
* Sets the content of the register.
|
||||
* @param data the new register data to be written
|
||||
*/
|
||||
void setData(regdata_t data) { ovpplatform.setRegisterData(reg, data); }
|
||||
};
|
||||
|
||||
/**
|
||||
* \class OVPRegister
|
||||
* OVP-specific implementation of the RegisterManager.
|
||||
*/
|
||||
class OVPRegisterManager : public RegisterManager {
|
||||
public:
|
||||
/**
|
||||
* Returns the current instruction pointer.
|
||||
* @return the current eip
|
||||
*/
|
||||
virtual address_t getInstructionPointer()
|
||||
{
|
||||
return ovpplatform.getPC();
|
||||
}
|
||||
/**
|
||||
* Retruns the top address of the stack.
|
||||
* @return the starting address of the stack
|
||||
*/
|
||||
virtual address_t getStackPointer()
|
||||
{
|
||||
return ovpplatform.getSP();
|
||||
}
|
||||
/**
|
||||
* Retrieves the base ptr (holding the address of the
|
||||
* current stack frame).
|
||||
* @return the base pointer
|
||||
*/
|
||||
virtual address_t getBasePointer()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __OVP_REGISTER_HPP__
|
||||
10
src/core/util/CMakeLists.txt
Normal file
10
src/core/util/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
set(SRCS
|
||||
SynchronizedCounter.cc
|
||||
Logger.hpp
|
||||
Logger.cc
|
||||
ProtoStream.cc
|
||||
)
|
||||
|
||||
add_library(util ${SRCS})
|
||||
|
||||
# FIXME: Add protobuf-dependency (required by ProtoStream.cc@line56)
|
||||
26
src/core/util/Logger.cc
Normal file
26
src/core/util/Logger.cc
Normal file
@ -0,0 +1,26 @@
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <time.h>
|
||||
|
||||
#include "Logger.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
void Logger::timestamp()
|
||||
{
|
||||
(*m_pDest) << "[" << m_description;
|
||||
if (m_showTime) {
|
||||
time_t rawtime;
|
||||
struct tm* timeinfo;
|
||||
char buffer[80];
|
||||
|
||||
time(&rawtime);
|
||||
timeinfo = localtime(&rawtime);
|
||||
|
||||
strftime(buffer, 80, "%H:%M:%S", timeinfo);
|
||||
(*m_pDest) << " " << buffer;
|
||||
}
|
||||
(*m_pDest) << "] ";
|
||||
}
|
||||
|
||||
} // end-of-namespace: fail
|
||||
53
src/core/util/Logger.hpp
Normal file
53
src/core/util/Logger.hpp
Normal file
@ -0,0 +1,53 @@
|
||||
#ifndef __LOGGER_HPP__
|
||||
#define __LOGGER_HPP__
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class Logger
|
||||
* Provides logging mechanisms.
|
||||
*/
|
||||
class Logger {
|
||||
private:
|
||||
std::ostream* m_pDest;
|
||||
std::string m_description;
|
||||
bool m_showTime;
|
||||
void timestamp();
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param description Description shown alongside each log entry in
|
||||
* square brackets [ ]. Can be overridden in single add() calls.
|
||||
* @param show_time Show a timestamp with each log entry.
|
||||
* @param dest Stream to log into.
|
||||
*/
|
||||
Logger(const std::string& description = "Fail*", bool show_time = true,
|
||||
std::ostream& dest = std::cout)
|
||||
: m_pDest(&dest), m_description(description), m_showTime(show_time) { }
|
||||
/**
|
||||
* Change the default description which is shown alongside each log
|
||||
* entry in square brackets [ ].
|
||||
* @param descr The description text.
|
||||
*/
|
||||
void setDescription(const std::string& descr) { m_description = descr; }
|
||||
/**
|
||||
* Change the default option of show_time which shows a timestamp
|
||||
* each log entry.
|
||||
* @param choice The choice for show_time
|
||||
*/
|
||||
void showTime(bool choice) { m_showTime = choice; }
|
||||
/**
|
||||
* Add a new log entry.
|
||||
* @param v data to log
|
||||
* @return a \c std::ostream reference to continue streaming a longer log entry
|
||||
*/
|
||||
template<class T>
|
||||
inline std::ostream& operator <<(const T& v) { timestamp(); return (*m_pDest) << v; }
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __LOGGER_HPP__
|
||||
80
src/core/util/MemoryMap.hpp
Normal file
80
src/core/util/MemoryMap.hpp
Normal file
@ -0,0 +1,80 @@
|
||||
#ifndef __MEMORYMAP_HPP__
|
||||
#define __MEMORYMAP_HPP__
|
||||
|
||||
#ifdef BOOST_1_46_OR_NEWER
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
using namespace boost::icl;
|
||||
#endif
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "sal/SALConfig.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class MemoryMap
|
||||
* An efficient container for memory maps with holes.
|
||||
*/
|
||||
class MemoryMap {
|
||||
private:
|
||||
#ifdef BOOST_1_46_OR_NEWER
|
||||
typedef interval<address_t>::type address_interval;
|
||||
typedef interval_set<address_t>::type address_set;
|
||||
|
||||
address_set as;
|
||||
public:
|
||||
MemoryMap() { }
|
||||
void clear() { as.clear(); }
|
||||
void add(address_t addr, int size) { as.add(address_interval(addr, addr+size-1)); }
|
||||
void isMatching(address_t addr, int size) { return intersects(as, address_interval(addr, addr+size-1)); }
|
||||
#endif
|
||||
std::set<address_t> as;
|
||||
public:
|
||||
MemoryMap() { }
|
||||
/**
|
||||
* Clears the map.
|
||||
*/
|
||||
void clear() { as.clear(); }
|
||||
/**
|
||||
* Adds one or a sequence of addresses to the map.
|
||||
*/
|
||||
void add(address_t addr, int size = 1)
|
||||
{
|
||||
for (int i = 0; i < size; ++i) {
|
||||
as.insert(addr + i);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Determines whether a given memory access at address \a addr with width
|
||||
* \a size hits the map.
|
||||
*/
|
||||
bool isMatching(address_t addr, int size = 1)
|
||||
{
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (as.find(addr + i) != as.end()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* The (STL-style) iterator of this class used to iterate over all
|
||||
* addresses in this map.
|
||||
*/
|
||||
typedef std::set<address_t>::iterator iterator;
|
||||
/**
|
||||
* Returns an (STL-style) iterator to the beginning of the internal data
|
||||
* structure.
|
||||
*/
|
||||
iterator begin() { return as.begin(); }
|
||||
/**
|
||||
* Returns an (STL-style) iterator to the end of the interal data
|
||||
* structure.
|
||||
*/
|
||||
iterator end() { return as.end(); }
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __MEMORYMAP_HPP__
|
||||
62
src/core/util/ProtoStream.cc
Normal file
62
src/core/util/ProtoStream.cc
Normal file
@ -0,0 +1,62 @@
|
||||
#include "ProtoStream.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
ProtoOStream::ProtoOStream(std::ostream *outfile) : m_outfile(outfile)
|
||||
{
|
||||
m_log.setDescription("ProtoStream");
|
||||
// TODO: log-Level?
|
||||
m_log.showTime(false);
|
||||
}
|
||||
|
||||
bool ProtoOStream::writeMessage(google::protobuf::Message* m)
|
||||
{
|
||||
m_size = htonl(m->ByteSize());
|
||||
m_outfile->write(reinterpret_cast<char*>(&m_size), sizeof(m_size));
|
||||
|
||||
if (m_outfile->bad()) {
|
||||
m_log << "Could not write to file!" << std::endl;
|
||||
// TODO: log-Level?
|
||||
return false;
|
||||
}
|
||||
|
||||
m->SerializeToOstream(m_outfile);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ProtoIStream::ProtoIStream(std::istream *infile) : m_infile(infile)
|
||||
{
|
||||
m_log.setDescription("ProtoStream");
|
||||
// TODO: log-Level?
|
||||
m_log.showTime(false);
|
||||
}
|
||||
|
||||
void ProtoIStream::reset()
|
||||
{
|
||||
m_infile->clear();
|
||||
m_infile->seekg(0, std::ios::beg);
|
||||
}
|
||||
|
||||
bool ProtoIStream::getNext(google::protobuf::Message* m)
|
||||
{
|
||||
m_infile->read(reinterpret_cast<char*>(&m_size), sizeof(m_size));
|
||||
if (!m_infile->good())
|
||||
return false;
|
||||
m_size = ntohl(m_size);
|
||||
|
||||
// FIXME reuse buffer (efficiency)
|
||||
// FIXME new[] may fail (i.e., return 0)
|
||||
char *buf = new char[m_size];
|
||||
m_infile->read(buf, m_size);
|
||||
if (!m_infile->good())
|
||||
// FIXME we're leaking buf[]
|
||||
return false;
|
||||
std::string st(buf, m_size);
|
||||
m->ParseFromString(st);
|
||||
|
||||
delete [] buf;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // end-of-namespace: fail
|
||||
81
src/core/util/ProtoStream.hpp
Normal file
81
src/core/util/ProtoStream.hpp
Normal file
@ -0,0 +1,81 @@
|
||||
/**
|
||||
* \brief One way to manage large protobuf-messages
|
||||
*
|
||||
* Google protobuf is not designed for large messages. That leads to
|
||||
* much memory which is consumed by processing large messages. To
|
||||
* solve this problem the ProtoStream class stores the protobuf-messages
|
||||
* in an other way. Instead of using the repeat-function the data is
|
||||
* written sequentially in a file. Written in the format:
|
||||
*
|
||||
* \code
|
||||
* | 4 bytes length-information of the first message | first message
|
||||
* | 4 bytes length-information of the second message | second message
|
||||
* | ...
|
||||
* \endcode
|
||||
*/
|
||||
|
||||
#ifndef __PROTOSTREAM_HPP__
|
||||
#define __PROTOSTREAM_HPP__
|
||||
|
||||
#include <iostream>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <google/protobuf/message.h>
|
||||
|
||||
#include "Logger.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class ProtoOStream
|
||||
*
|
||||
* This class can be used to sequentially write a large number of
|
||||
* protocol buffer messages to a \c std::ostream.
|
||||
*/
|
||||
class ProtoOStream {
|
||||
private:
|
||||
uint32_t m_size; // TODO: comments needed here
|
||||
Logger m_log;
|
||||
std::ostream* m_outfile;
|
||||
public:
|
||||
ProtoOStream(std::ostream *outfile);
|
||||
virtual ~ProtoOStream() { }
|
||||
/**
|
||||
* Writes a message to a file.
|
||||
* @param m The protobuf-message to be written
|
||||
* @return Returns \c true on success, \c false otherwise
|
||||
*/
|
||||
bool writeMessage(google::protobuf::Message* m);
|
||||
};
|
||||
|
||||
/**
|
||||
* \class ProtoIStream
|
||||
*
|
||||
* This class can be used to read protocol buffer messages sequentially
|
||||
* from a \c std::istream.
|
||||
*/
|
||||
class ProtoIStream {
|
||||
private:
|
||||
uint32_t m_size; // TODO: comments needed here
|
||||
long m_sizeOfInfile;
|
||||
Logger m_log;
|
||||
std::istream *m_infile;
|
||||
public:
|
||||
ProtoIStream(std::istream *infile);
|
||||
virtual ~ProtoIStream() { }
|
||||
/**
|
||||
* Resets the position of the get pointer. After that \c getNext()
|
||||
* reads the first message again.
|
||||
*/
|
||||
void reset();
|
||||
/**
|
||||
* Reads the next protobuf message from the input stream.
|
||||
* @param m The output protobuf message
|
||||
* @return Returns \c true on success, \c false otherwise
|
||||
*/
|
||||
bool getNext(google::protobuf::Message* m);
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __PROTOSTREAM_HPP__
|
||||
29
src/core/util/SynchronizedCounter.cc
Normal file
29
src/core/util/SynchronizedCounter.cc
Normal file
@ -0,0 +1,29 @@
|
||||
#include "SynchronizedCounter.hpp"
|
||||
|
||||
namespace fail {
|
||||
|
||||
int SynchronizedCounter::increment()
|
||||
{
|
||||
#ifndef __puma
|
||||
boost::unique_lock<boost::mutex> lock(m_mutex);
|
||||
#endif
|
||||
return ++m_counter;
|
||||
}
|
||||
|
||||
int SynchronizedCounter::decrement()
|
||||
{
|
||||
#ifndef __puma
|
||||
boost::unique_lock<boost::mutex> lock(m_mutex);
|
||||
#endif
|
||||
return --m_counter;
|
||||
}
|
||||
|
||||
int SynchronizedCounter::getValue()
|
||||
{
|
||||
#ifndef __puma
|
||||
boost::unique_lock<boost::mutex> lock(m_mutex);
|
||||
#endif
|
||||
return m_counter;
|
||||
}
|
||||
|
||||
} // end-of-namespace: fail
|
||||
38
src/core/util/SynchronizedCounter.hpp
Normal file
38
src/core/util/SynchronizedCounter.hpp
Normal file
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* \brief Thread safe (synchronized) counter.
|
||||
*/
|
||||
|
||||
#ifndef __SYNCHRONIZED_COUNTER_HPP__
|
||||
#define __SYNCHRONIZED_COUNTER_HPP__
|
||||
|
||||
#ifndef __puma
|
||||
#include <boost/thread.hpp>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
namespace fail {
|
||||
|
||||
/**
|
||||
* \class ssd
|
||||
*
|
||||
* Provides a thread safe (synchronized) counter. When a method is called,
|
||||
* the internal mutex is automatically locked. On return, the lock is
|
||||
* automatically released ("scoped lock").
|
||||
*/
|
||||
class SynchronizedCounter {
|
||||
private:
|
||||
int m_counter;
|
||||
#ifndef __puma
|
||||
boost::mutex m_mutex; //! The mutex to synchronise on
|
||||
#endif
|
||||
public:
|
||||
SynchronizedCounter() : m_counter(0) { }
|
||||
|
||||
int increment();
|
||||
int decrement();
|
||||
int getValue();
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __SYNCHRONIZED_COUNTER_HPP__
|
||||
116
src/core/util/SynchronizedMap.hpp
Normal file
116
src/core/util/SynchronizedMap.hpp
Normal file
@ -0,0 +1,116 @@
|
||||
/**
|
||||
* \brief Map class that has thread synchronisation
|
||||
*/
|
||||
|
||||
#ifndef __SYNCHRONIZED_MAP_HPP__
|
||||
#define __SYNCHRONIZED_MAP_HPP__
|
||||
|
||||
#include <map>
|
||||
|
||||
#ifndef __puma
|
||||
#include <boost/thread.hpp>
|
||||
#endif
|
||||
|
||||
// TODO: We should consider to use Aspects for synchronisation primitives.
|
||||
|
||||
namespace fail {
|
||||
|
||||
template <typename Tkey, typename Tvalue>
|
||||
class SynchronizedMap {
|
||||
private:
|
||||
typedef std::map< Tkey, Tvalue > Tmap;
|
||||
typedef typename Tmap::iterator Tit;
|
||||
|
||||
Tmap m_map; //! Use STL map to store data
|
||||
#ifndef __puma
|
||||
boost::mutex m_mutex; //! The mutex to synchronise on
|
||||
#endif
|
||||
|
||||
int nextpick;
|
||||
enum { pick_window_size = 500 };
|
||||
|
||||
public:
|
||||
SynchronizedMap() : nextpick(0) { }
|
||||
int Size()
|
||||
{
|
||||
#ifndef __puma
|
||||
boost::unique_lock<boost::mutex> lock(m_mutex);
|
||||
#endif
|
||||
return m_map.size();
|
||||
}
|
||||
/**
|
||||
* Retrieves one element from the map from a small window at the beginning.
|
||||
* @return a pointer to the element, or \c NULL if empty
|
||||
*/
|
||||
Tvalue pickone()
|
||||
{
|
||||
#ifndef __puma
|
||||
boost::unique_lock<boost::mutex> lock(m_mutex);
|
||||
#endif
|
||||
if (m_map.size() == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// XXX not really elegant: linear complexity
|
||||
typename Tmap::iterator it = m_map.begin();
|
||||
for (int i = 0; i < nextpick; ++i) {
|
||||
++it;
|
||||
if (it == m_map.end()) {
|
||||
it = m_map.begin();
|
||||
nextpick = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
++nextpick;
|
||||
if (nextpick >= pick_window_size) {
|
||||
nextpick = 0;
|
||||
}
|
||||
return it->second;
|
||||
} // Lock is automatically released here
|
||||
/**
|
||||
* Add data to the map, return false if already present
|
||||
* @param key Map key
|
||||
* @param value value according to key
|
||||
* @return false if key already present
|
||||
*/
|
||||
bool insert(const Tkey& key, const Tvalue& value)
|
||||
{
|
||||
// Acquire lock on the queue
|
||||
#ifndef __puma
|
||||
boost::unique_lock<boost::mutex> lock(m_mutex);
|
||||
#endif
|
||||
if( m_map.find(key) == m_map.end() ){ // not present, add it
|
||||
m_map[key] = value;
|
||||
return true;
|
||||
}else{ // item is already in, oops
|
||||
return false;
|
||||
}
|
||||
|
||||
} // Lock is automatically released here
|
||||
|
||||
/**
|
||||
* Remove value from the map.
|
||||
* @param key The Map key to remove
|
||||
* @return false if key was not present
|
||||
*
|
||||
*/
|
||||
bool remove(const Tkey& key, Tvalue& value)
|
||||
{
|
||||
// Acquire lock on the queue
|
||||
#ifndef __puma
|
||||
boost::unique_lock<boost::mutex> lock(m_mutex);
|
||||
#endif
|
||||
Tit iterator;
|
||||
if ((iterator = m_map.find(key)) != m_map.end()) {
|
||||
value = iterator->second;
|
||||
m_map.erase(iterator);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} // Lock is automatically released here
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __SYNCHRONIZED_MAP_HPP__
|
||||
96
src/core/util/SynchronizedQueue.hpp
Normal file
96
src/core/util/SynchronizedQueue.hpp
Normal file
@ -0,0 +1,96 @@
|
||||
/**
|
||||
* \brief Queue class that has thread synchronisation.
|
||||
*/
|
||||
|
||||
#ifndef __SYNCHRONIZED_QUEUE_HPP__
|
||||
#define __SYNCHRONIZED_QUEUE_HPP__
|
||||
|
||||
#include <queue>
|
||||
|
||||
#ifndef __puma
|
||||
#include <boost/thread.hpp>
|
||||
#endif
|
||||
|
||||
namespace fail {
|
||||
|
||||
template <typename T>
|
||||
class SynchronizedQueue { // Adapted from: http://www.quantnet.com/cplusplus-multithreading-boost/
|
||||
private:
|
||||
std::queue<T> m_queue; //!< Use STL queue to store data
|
||||
#ifndef __puma
|
||||
boost::mutex m_mutex; //!< The mutex to synchronise on
|
||||
boost::condition_variable m_cond; //!< The condition to wait for
|
||||
#endif
|
||||
public:
|
||||
int Size()
|
||||
{
|
||||
#ifndef __puma
|
||||
boost::unique_lock<boost::mutex> lock(m_mutex);
|
||||
#endif
|
||||
return m_queue.size();
|
||||
}
|
||||
// Add data to the queue and notify others
|
||||
void Enqueue(const T& data)
|
||||
{
|
||||
// Acquire lock on the queue
|
||||
#ifndef __puma
|
||||
boost::unique_lock<boost::mutex> lock(m_mutex);
|
||||
#endif
|
||||
// Add the data to the queue
|
||||
m_queue.push(data);
|
||||
// Notify others that data is ready
|
||||
#ifndef __puma
|
||||
m_cond.notify_one();
|
||||
#endif
|
||||
} // Lock is automatically released here
|
||||
|
||||
/**
|
||||
* Get data from the queue. Wait for data if not available
|
||||
*/
|
||||
T Dequeue()
|
||||
{
|
||||
// Acquire lock on the queue
|
||||
#ifndef __puma
|
||||
boost::unique_lock<boost::mutex> lock(m_mutex);
|
||||
#endif
|
||||
// When there is no data, wait till someone fills it.
|
||||
// Lock is automatically released in the wait and obtained
|
||||
// again after the wait
|
||||
#ifndef __puma
|
||||
while (m_queue.size() == 0)
|
||||
m_cond.wait(lock);
|
||||
#endif
|
||||
// Retrieve the data from the queue
|
||||
T result=m_queue.front(); m_queue.pop();
|
||||
return result;
|
||||
} // Lock is automatically released here
|
||||
|
||||
|
||||
/**
|
||||
* Get data from the queue. Non blocking variant.
|
||||
* @param d Pointer to copy queue element to
|
||||
* @return false if no element in queue
|
||||
*
|
||||
*/
|
||||
bool Dequeue_nb(T& d)
|
||||
{
|
||||
// Acquire lock on the queue
|
||||
#ifndef __puma
|
||||
boost::unique_lock<boost::mutex> lock(m_mutex);
|
||||
#endif
|
||||
// When there is no data, return false.
|
||||
// Lock is automatically released in the wait and obtained
|
||||
// again after the wait
|
||||
if (m_queue.size() > 0) {
|
||||
// Retrieve the data from the queue
|
||||
d = m_queue.front(); m_queue.pop();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} // Lock is automatically released here
|
||||
};
|
||||
|
||||
} // end-of-namespace: fail
|
||||
|
||||
#endif // __SYNCHRONIZED_QUEUE_HPP__
|
||||
Reference in New Issue
Block a user