From 13cd951a87d52eaa2c34f8dbf6bf21e289f703a4 Mon Sep 17 00:00:00 2001
From: unzner <unzner@8c4709b5-6ec9-48aa-a5cd-a96041d1645a>
Date: Sat, 9 Jun 2012 17:31:32 +0000
Subject: [PATCH] Bugfix in BufferCache, added some security checks, plus minor
 stuff in doc/howtobuild.txt

git-svn-id: https://www4.informatik.uni-erlangen.de/i4svn/danceos/trunk/devel/fail@1323 8c4709b5-6ec9-48aa-a5cd-a96041d1645a
---
 doc/howto-build.txt                   |  8 ++--
 src/core/sal/BufferCache.cc           | 59 +++++++++++++++++++--------
 src/core/sal/BufferCache.hpp          | 47 +++++++++++----------
 src/core/sal/EventList.cc             | 15 +++++--
 src/core/sal/EventList.hpp            |  1 +
 src/core/sal/bochs/BochsController.cc | 17 +++-----
 src/experiments/l4-sys/experiment.cc  | 53 +++++++++++++++++-------
 7 files changed, 127 insertions(+), 73 deletions(-)

diff --git a/doc/howto-build.txt b/doc/howto-build.txt
index 4216def3..da5f50a3 100644
--- a/doc/howto-build.txt
+++ b/doc/howto-build.txt
@@ -97,7 +97,7 @@ Build Bochs
 ===========
 For the first time
 ------------------
-cd ../bochs
+cd ../simulators/bochs
 > Sufficient:
 ./configure --prefix=$(echo ~/localroot/usr) --enable-{cpu-level=6,ne2000,trace-cache,gdb-stub} --disable-docbook
 > More optimised:
@@ -136,17 +136,17 @@ Debug build
 -----------
 > Configure Bochs to use debugging-related compiler flags:
 cd ../bochs
-CXXFLAGS="-g -O0" ./configure --prefix=... ... (see above)
+CFLAGS="-g -O0" CXXFLAGS="-g -O0" ./configure --prefix=... ... (see above)
 
 Profiling-based optimization build
 ----------------------------------
 > FIXME: ag++ needs to be run with --keep_woven
 > Configure Bochs to use compiler flags to enable profiling:
 cd ../bochs
-CXXFLAGS="-fprofile-generate" LDFLAGS="-fprofile-generate" ./configure --prefix=... ... (see above)
+CFLAGS="-fprofile-generate" CXXFLAGS="-fprofile-generate" LDFLAGS="-fprofile-generate" ./configure --prefix=... ... (see above)
 > Build Bochs normally, and run it.
 > Configure Bochs to use compiler flags to enable optimizations based on profiling results:
-CXXFLAGS="-fprofile-use -Wcoverage-mismatch" LDFLAGS="-fprofile-use" ./configure --prefix=... ... (see above)
+CFLAGS="-fprofile-use -Wcoverage-mismatch" CXXFLAGS="-fprofile-use -Wcoverage-mismatch" LDFLAGS="-fprofile-use" ./configure --prefix=... ... (see above)
 
 Benchmarking
 ------------
diff --git a/src/core/sal/BufferCache.cc b/src/core/sal/BufferCache.cc
index cc876f77..8987ef49 100644
--- a/src/core/sal/BufferCache.cc
+++ b/src/core/sal/BufferCache.cc
@@ -1,27 +1,28 @@
+#include <algorithm>
+#include <vector>
 #include "BufferCache.hpp"
 #include "Event.hpp"
+#include "EventList.hpp"
 
 namespace fail {
 
 template<class T>
-int BufferCache<T>::add(T val)
+void BufferCache<T>::add(T val)
 {
-	size_t new_size = getCount() + 1;
-	size_t new_last_index = getCount();
+	int new_size = getCount() + 1;
+	int new_last_index = getCount();
 
 	int res = reallocate_buffer(new_size);
-	if (res == 0) {
-		set(new_last_index, val);
-	}
+	assert (res == 0 && "FATAL ERROR: Could not add event to cache");
 
-	return res;
+	set(new_last_index, val);
 }
 
 template<class T>
-int BufferCache<T>::remove(T val)
+void BufferCache<T>::remove(T val)
 {
 	bool do_remove = false;
-	for (size_t i = 0; i < getCount(); i++) {
+	for (int i = 0; i < getCount(); i++) {
 		if (get(i) == val) {
 			do_remove = true;
 		}
@@ -32,13 +33,11 @@ int BufferCache<T>::remove(T val)
 		}
 	}
 
-	int res = 0;
 	if (do_remove) {
-		size_t new_size = getCount() - 1;
-		res = reallocate_buffer(new_size);
+		int new_size = getCount() - 1;
+		int res = reallocate_buffer(new_size);
+		assert (res == 0 && "FATAL ERROR: Could not remove event from cache");
 	}
-
-	return res;
 }
 
 template<class T>
@@ -52,19 +51,25 @@ void BufferCache<T>::clear()
 template<class T>
 int BufferCache<T>::erase(int idx)
 {
-	for (size_t i = idx; i < getCount() - 1; i++) {
+	if(idx < 0 || idx >= getCount())
+		return -2;
+
+	for (int i = idx; i < getCount() - 1; i++) {
 		set(i, get(i + 1));
 	}
 
-	size_t new_size = getCount() - 1;
+	int 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)
+int BufferCache<T>::reallocate_buffer(int new_size)
 {
+	if (new_size < 0)
+		return 20;
+
 	if (new_size == 0) {
 		clear();
 		return 0;
@@ -77,6 +82,26 @@ int BufferCache<T>::reallocate_buffer(size_t new_size)
 	return 0;
 }
 
+template<class T>
+int BufferCache<T>::makeActive(EventList &ev_list, int idx)
+{
+	assert(idx < getCount() &&
+		   "FATAL ERROR: Index larger than cache!");
+	T ev = get(idx);
+	assert(ev && "FATAL ERROR: Object pointer cannot be NULL!");
+	ev->decreaseCounter();
+	if (ev->getCounter() > 0) {
+		return ++idx;
+	}
+	ev->resetCounter();
+	// Note: This is the one and only situation in which remove() should NOT
+	//       store the removed item in the delete-list.
+	EventList::iterator it = std::find(ev_list.begin(), ev_list.end(), static_cast<BaseEvent*>(ev));
+	ev_list.m_remove(it, true); // remove event from buffer-list
+	ev_list.m_FireList.push_back(ev);
+	return erase(idx);
+}
+
 // Declare whatever instances of the template you are going to use here:
 template class BufferCache<BPEvent*>;
 
diff --git a/src/core/sal/BufferCache.hpp b/src/core/sal/BufferCache.hpp
index c08c4efa..5e413686 100644
--- a/src/core/sal/BufferCache.hpp
+++ b/src/core/sal/BufferCache.hpp
@@ -3,11 +3,10 @@
 
 #include <stdlib.h>
 
-// FIXME: (Maybe) This should be located in utils, because
-//        it's "Fail*-independend"...?
-
 namespace fail {
 
+class EventList;
+
 /**
  * \class BufferCache
  *
@@ -15,11 +14,8 @@ namespace fail {
  *
  * 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")
+ * To keep the code easily readable, some buffer management methods
+ * perform suboptimally (remove() and erase() have linear complexity).
  * 
  * FIXME: Why not using std::vector? ("A simple dynamic array")
  */
@@ -28,21 +24,21 @@ class BufferCache {
 private:
 	// TODO: comments ("//!<") needed!
 	T *m_Buffer;
-	size_t m_BufferCount;
-protected:
+	int m_BufferCount;
 	/**
-	 * Changes the current length of the array. Should be inlined.
+	 * Changes m_BufferCount. Should be inlined.
 	 * @param new_count the new array length
 	 */
-	inline void setCount(size_t new_count) { m_BufferCount = new_count; }
+	inline void setCount(int new_count) { if(new_count >= 0) m_BufferCount = new_count; }
+protected:
 	/**
 	 * 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)
+	 * @return 0 if successful, an error code otherwise (10 if realloc() fails, 20 for an invalid new size)
 	 */
-	inline int reallocate_buffer(size_t new_size);
+	inline int reallocate_buffer(int new_size);
 public:
 	BufferCache()
 		: m_Buffer(NULL), m_BufferCount(0) {}
@@ -50,19 +46,17 @@ public:
 	/**
 	 * 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);
+	void 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);
+	void 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
+	 * @return a pointer to the given element's successor if successful, a negative value otherwise
 	 */
 	int erase(int i);
 	/**
@@ -74,18 +68,27 @@ public:
 	 * @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]; }
+	inline T get(int idx) { return (idx >= 0 && idx < getCount() ? m_Buffer[idx] : NULL); }
 	/**
 	 * 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; }
+	inline void set(int idx, T val) { if(idx >= 0 && idx < getCount()) 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; }
+	inline int getCount() { return m_BufferCount; }
+	/**
+	 * Acts as a replacement for EventList::makeActive, manipulating
+	 * the buffer cache exclusively. EventList::fireActiveEvents needs
+	 * to be called to fire the active events (see there).
+	 * This method is declared as a friend method in EventList.
+	 * @param idx the index of the event to trigger
+	 * @returns an updated index which can be used to update a loop counter
+	 */
+	int makeActive(EventList &ev_list, int idx);
 };
 
 } // end-of-namespace: fail
diff --git a/src/core/sal/EventList.cc b/src/core/sal/EventList.cc
index 0d27f7da..571a262c 100644
--- a/src/core/sal/EventList.cc
+++ b/src/core/sal/EventList.cc
@@ -63,16 +63,23 @@ 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
+		// from the buffer list to the fire-list. Therefore 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);
+
+		// Cached events have their own BufferCache<T>::makeActive() implementation, which
+		// calls this method and afterwards erase() in the cache class. This is why, when
+		// called from any kind of makeActive() method, it is unnecessary to call
+		// BufferCache<T>::remove() from m_remove().
+
+		// NOTE: in case the semantics of skip_deletelist change, please adapt the following lines
+		BPEvent *bp_ev;
+		if((bp_ev = dynamic_cast<BPEvent*>(*it)) != NULL)
+			m_Bp_cache.remove(bp_ev);
 	}
 
-	BPEvent *bp_ev;
-	if((bp_ev = dynamic_cast<BPEvent*>(*it)) != NULL)
-		m_Bp_cache.remove(bp_ev);
 	return (m_BufferList.erase(it));
 }
 
diff --git a/src/core/sal/EventList.hpp b/src/core/sal/EventList.hpp
index 828dc7d8..8e3c8d0f 100644
--- a/src/core/sal/EventList.hpp
+++ b/src/core/sal/EventList.hpp
@@ -54,6 +54,7 @@ class EventList
 		deletelist_t m_DeleteList; //!< the deleted events (used temporarily)
 		BaseEvent* m_pFired; //!< the recently fired Event-object
 		BufferCache<BPEvent*> m_Bp_cache;
+		friend int BufferCache<BPEvent*>::makeActive(EventList &ev_list, int idx);
 	public:
 		/**
 		 * The iterator of this class used to loop through the list of
diff --git a/src/core/sal/bochs/BochsController.cc b/src/core/sal/bochs/BochsController.cc
index fa1f6b82..e012c45a 100644
--- a/src/core/sal/bochs/BochsController.cc
+++ b/src/core/sal/bochs/BochsController.cc
@@ -119,24 +119,17 @@ void BochsController::onInstrPtrChanged(address_t instrPtr, address_t address_sp
 #endif
 	//this code is highly optimised for the average case, so it me appear a bit ugly
 	bool do_fire = false;
-	unsigned i = 0;
+	int 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;
-				}
-			}
+			i = buffer_cache->makeActive(m_EvList, i);
+			assert(i >= 0 &&
+					   "FATAL ERROR: Could not erase BPEvent from cache");
+
 			// 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
diff --git a/src/experiments/l4-sys/experiment.cc b/src/experiments/l4-sys/experiment.cc
index a0c68713..89e31df8 100644
--- a/src/experiments/l4-sys/experiment.cc
+++ b/src/experiments/l4-sys/experiment.cc
@@ -32,12 +32,29 @@ using namespace fail;
   save, and restore. Enable these in the configuration.
 #endif
 
+typedef struct __trace_instr_type {
+	address_t trigger_addr;
+	unsigned bp_counter;
+} trace_instr;
+
+ostream& operator<<(ostream& out, const trace_instr &val) {
+	out << val.trigger_addr << "," << val.bp_counter;
+	return out;
+}
+istream& operator>>(istream& in, trace_instr &val) {
+	in >> val.trigger_addr;
+	//skip the comma
+	in.ignore(1);
+	in >> val.bp_counter;
+	return in;
+}
+
 char const * const state_folder  = "l4sys.state";
 char const * const instr_list_fn = "ip.list";
 char const * const golden_run_fn = "golden.out";
 address_t const aspace = 0x01e00000;
 string output;
-vector<address_t> instr_list;
+vector<trace_instr> instr_list;
 string golden_run;
 //the program needs to run 5 times without a fault
 const unsigned times_run = 5;
@@ -85,8 +102,6 @@ bool L4SysExperiment::run()
 
 	log << "startup" << endl;
 
-#ifdef PREPARE_EXPERIMENT
-
 	struct stat teststruct;
 	// STEP 1: run until interesting function starts, and save state
 	if (stat(state_folder, &teststruct) == -1) {
@@ -115,14 +130,25 @@ bool L4SysExperiment::run()
 		ofstream instr_list_file(instr_list_fn);
 		instr_list_file << hex;
 		bp.setWatchInstructionPointer(ANY_ADDR);
+
+		map<address_t, unsigned> times_called_map;
 		while (bp.getTriggerInstructionPointer() != L4SYS_FUNC_EXIT) {
 			simulator.addEventAndWait(&bp);
 			//short sanity check
-			address_t curr_instr = bp.getTriggerInstructionPointer();
+			address_t curr_addr = bp.getTriggerInstructionPointer();
 			assert(
-					curr_instr == simulator.getRegisterManager().getInstructionPointer());
-			instr_list.push_back(curr_instr);
-			instr_list_file << curr_instr << endl;
+					curr_addr == simulator.getRegisterManager().getInstructionPointer());
+
+			unsigned times_called = times_called_map[curr_addr];
+			times_called++;
+			times_called_map[curr_addr] = times_called;
+
+			trace_instr new_instr;
+			new_instr.trigger_addr = curr_addr;
+			new_instr.bp_counter = times_called;
+			instr_list.push_back(new_instr);
+
+			instr_list_file << new_instr << endl;
 		}
 		log << "saving instructions triggered during normal execution" << endl;
 		instr_list_file.close();
@@ -130,7 +156,7 @@ bool L4SysExperiment::run()
 		ifstream instr_list_file(instr_list_fn);
 		instr_list_file >> hex;
 		while (!instr_list_file.eof()) {
-			address_t curr_instr;
+			trace_instr curr_instr;
 			instr_list_file >> curr_instr;
 			instr_list.push_back(curr_instr);
 		}
@@ -186,10 +212,8 @@ bool L4SysExperiment::run()
 		output.reserve(flen);
 	}
 
-#endif
-
 	// STEP 4: The actual experiment.
-	for (int i = 0; i < 1/*L4SYS_NUMINSTR*/; i++) {
+	for (int i = 0; i < L4SYS_NUMINSTR; i++) {
 		log << "restoring state" << endl;
 		simulator.restore(state_folder);
 
@@ -206,7 +230,8 @@ bool L4SysExperiment::run()
 		log << "job " << id << " instr " << instr_offset << " bit "
 				<< bit_offset << endl;
 
-		bp.setWatchInstructionPointer(instr_list[instr_offset]);
+		bp.setWatchInstructionPointer(instr_list[instr_offset].trigger_addr);
+		bp.setCounter(instr_list[instr_offset].bp_counter);
 		simulator.addEvent(&bp);
 		//and log the output
 		waitIOOrOther(true);
@@ -227,10 +252,10 @@ bool L4SysExperiment::run()
 				<< endl;
 
 		// sanity check (only works if we're working with an instruction trace)
-		if (injection_ip != instr_list[instr_offset]) {
+		if (injection_ip != instr_list[instr_offset].trigger_addr) {
 			stringstream ss;
 			ss << "SANITY CHECK FAILED: " << injection_ip << " != "
-					<< instr_list[instr_offset] << endl;
+					<< instr_list[instr_offset].trigger_addr << endl;
 			log << ss.str();
 			param.msg.set_resulttype(param.msg.UNKNOWN);
 			param.msg.set_resultdata(injection_ip);
-- 
GitLab