diff --git a/doc/manual.tex b/doc/manual.tex index 39225e8a0..5791b7ba3 100644 --- a/doc/manual.tex +++ b/doc/manual.tex @@ -2,6 +2,7 @@ \usepackage[utf8]{inputenc} \usepackage{amssymb} +\usepackage[colorlinks,linkcolor=black]{hyperref} \title{Nidhugg -- Manual\\\large{Version %%NIDHUGGVERSION%%}} \author{} @@ -365,6 +366,38 @@ \subsubsection{Transformation Switches}\label{sec:transform:switches} high-level language. \end{description} +\subsubsection{Tracing Switches} + +Nidhugg has some switches that can show how it is exploring a program +and what operations it considers racing. There operations output graphs +in Graphviz\footnote{\url{https://graphviz.org}} DOT format. + +\begin{description} +\item{\texttt{--dump-tree=$OUTFILE$}} +% + After analysis, write a graph that visualises the exploration tree to + $OUTFILE$. + +\item{\texttt{--dump-traces=$OUTFILE$}} +% + After analysis, write a graph of the happens-before orders of all explored + traces to $OUTFILE$. +% + \limitsupport{SC, TSO, PSO} + +\item{\texttt{--dump-spec=$OUTFILE$}} +% + This option is primarily useful to developers of Nidhugg. After analysis, + write a minimised \texttt{DPORDriver\_test::trace\_set\_\hspace{0pt}spec} + characterising the explored set of traces, excluding sleepset-blocked ones, to + $OUTFILE$. Useful for creating regression tests after an issue has been fixed, + and the test program is not small enough to make writing a + \texttt{DPORDriver\_test::trace\_set\_spec} by hand feasible. +% + \limitsupport{SC, TSO, PSO} + +\end{description} + \subsubsection{Exit Status} Exit status for Nidhugg. diff --git a/src/CPid.cpp b/src/CPid.cpp index b054d4eba..a8453161e 100644 --- a/src/CPid.cpp +++ b/src/CPid.cpp @@ -121,6 +121,10 @@ int CPid::get_aux_index() const{ return aux_idx; } +int CPid::get_child_index() const{ + return proc_seq.back(); +} + CPidSystem::CPidSystem(){ real_children.push_back({}); aux_children.push_back({}); diff --git a/src/CPid.h b/src/CPid.h index 44613e364..a7a06af33 100644 --- a/src/CPid.h +++ b/src/CPid.h @@ -71,6 +71,9 @@ class CPid{ */ int get_aux_index() const; + /* Returns pn where this CPid is . */ + int get_child_index() const; + std::string to_string() const; /* Comparison implements a total order over CPids. */ diff --git a/src/Configuration.cpp b/src/Configuration.cpp index d0b4d8f58..036e2f9f3 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -99,6 +99,24 @@ static llvm::cl::opt cl_debug_print_on_reset ("debug-print-on-reset",llvm::cl::Hidden, llvm::cl::desc("Print debug info after exploring each trace.")); +static llvm::cl::OptionCategory cl_dump_cat +("Trace Dumping"); + +static llvm::cl::opt cl_dump_traces +("dump-traces",llvm::cl::NotHidden,llvm::cl::cat(cl_dump_cat), + llvm::cl::value_desc("FILE"), + llvm::cl::desc("Write graph of explored equivalence classes to FILE.")); + +static llvm::cl::opt cl_dump_tree +("dump-tree",llvm::cl::NotHidden,llvm::cl::cat(cl_dump_cat), + llvm::cl::value_desc("FILE"), + llvm::cl::desc("Write graph of exploration tree to FILE.")); + +static llvm::cl::opt cl_dump_spec +("dump-spec",llvm::cl::NotHidden,llvm::cl::cat(cl_dump_cat), + llvm::cl::value_desc("FILE"), + llvm::cl::desc("Write minimal trace_set_spec to FILE.")); + const std::set &Configuration::commandline_opts(){ static std::set opts = { "dpor-explore-all", @@ -113,7 +131,10 @@ const std::set &Configuration::commandline_opts(){ "no-spin-assume", "unroll", "print-progress", - "print-progress-estimate" + "print-progress-estimate", + "dump-traces", + "dump-tree", + "dump-spec", }; return opts; } @@ -137,6 +158,13 @@ void Configuration::assign_by_commandline(){ print_progress = cl_print_progress || cl_print_progress_estimate; print_progress_estimate = cl_print_progress_estimate; debug_print_on_reset = cl_debug_print_on_reset; + trace_dump_file = cl_dump_traces; + debug_collect_all_traces |= !cl_dump_traces.empty(); + tree_dump_file = cl_dump_tree; + debug_collect_all_traces |= !cl_dump_tree.empty(); + ee_store_trace |= !cl_dump_tree.empty(); + spec_dump_file = cl_dump_spec; + debug_collect_all_traces |= !cl_dump_spec.empty(); argv.resize(1); argv[0] = get_default_program_name(); for(std::string a : cl_program_arguments){ @@ -226,6 +254,14 @@ void Configuration::check_commandline(){ Debug::warn("Configuration::check_commandline:mm:robustness") << "WARNING: --robustness ignored under memory model " << mm << ".\n"; } + if (cl_dump_traces != ""){ + Debug::warn("Configuration::check_commandline:mm:dump-traces") + << "WARNING: --dump-traces not implemented for memory model " << mm << ".\n"; + } + if (cl_dump_spec != ""){ + Debug::warn("Configuration::check_commandline:mm:dump-spec") + << "WARNING: --dump-spec ignored under memory model " << mm << ".\n"; + } } if (cl_dpor_algorithm == Configuration::OPTIMAL && cl_memory_model != Configuration::SC diff --git a/src/Configuration.h b/src/Configuration.h index 2b32af7fa..b254224d6 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -184,6 +184,12 @@ class Configuration{ * traces. */ bool print_progress_estimate; + /* File to dump graph of equivalence classes to. */ + std::string trace_dump_file; + /* File to dump graph of exploration tree to. */ + std::string tree_dump_file; + /* File to dump minimal trace set specification to. */ + std::string spec_dump_file; /* The arguments that will be passed to the program under test */ std::vector argv; /* The default program name to send to the program under test as diff --git a/src/DPORDriver_test.cpp b/src/DPORDriver_test.cpp index fc140c07e..c743ea379 100644 --- a/src/DPORDriver_test.cpp +++ b/src/DPORDriver_test.cpp @@ -99,17 +99,16 @@ namespace DPORDriver_test { return res; } - int find(const IIDSeqTrace *_t, const IID &iid){ - const std::vector > &t = _t->get_computation(); - for(int i = 0; i < int(t.size()); ++i){ - if(t[i] == iid){ + int find(const IIDVCSeqTrace *t, const IID &iid){ + for(int i = 0; i < int(t->size()); ++i){ + if(t->get_iid(i) == iid){ return i; } } return -1; } - bool check_trace(const IIDSeqTrace *t, const trace_spec &spec){ + bool check_trace(const IIDVCSeqTrace *t, const trace_spec &spec){ for(unsigned i = 0; i < spec.size(); ++i){ int a_i = find(t,spec[i].a); int b_i = find(t,spec[i].b); @@ -122,11 +121,12 @@ namespace DPORDriver_test { /* Returns true iff the indices of the IIDs of each process is * strictly increasing along the computation of t. */ - bool all_clocks_increasing(const IIDSeqTrace *t){ + bool all_clocks_increasing(const IIDVCSeqTrace *t){ VClock pcs; - for(auto it = t->get_computation().begin(); it != t->get_computation().end(); ++it){ - if(it->get_index() <= pcs[it->get_pid()]) return false; - pcs[it->get_pid()] = it->get_index(); + for(unsigned i = 0; i < t->size(); ++i){ + const IID &iid = t->get_iid(i); + if(iid.get_index() <= pcs[iid.get_pid()]) return false; + pcs[iid.get_pid()] = iid.get_index(); } return true; } @@ -141,7 +141,7 @@ namespace DPORDriver_test { } bool retval = true; for(unsigned i = 0; i < res.all_traces.size(); ++i){ - if(!all_clocks_increasing(static_cast(res.all_traces[i]))){ + if(!all_clocks_increasing(static_cast(res.all_traces[i]))){ llvm::dbgs() << "DPORDriver_test::check_all_traces: " << "Non increasing instruction index in trace.\n"; retval = false; @@ -160,7 +160,7 @@ namespace DPORDriver_test { int prev_match = -1; for(unsigned j = 0; j < res.all_traces.size(); ++j){ if(res.all_traces[j]->is_blocked()) continue; - if(check_trace(static_cast(res.all_traces[j]),spec[i])){ + if(check_trace(static_cast(res.all_traces[j]),spec[i])){ if(found){ // Multiple traces match the same specification llvm::dbgs() << "DPORDriver_test::check_all_traces: Multiple traces (#" @@ -215,12 +215,12 @@ namespace DPORDriver_test { static bool trace_equiv(const Trace *a, const Trace *b){ if (a->get_errors().size() != b->get_errors().size()) return false; std::list bes; - for (Error *be : b->get_errors()) { - bes.push_back(be); + for (const std::unique_ptr &be : b->get_errors()) { + bes.push_back(be.get()); } - for (Error *ae : a->get_errors()) { - auto bei = std::find_if(bes.begin(), bes.end(), [ae](const Error *be){ - return error_equiv(ae, be); + for (const std::unique_ptr &ae : a->get_errors()) { + auto bei = std::find_if(bes.begin(), bes.end(), [&ae](const Error *be){ + return error_equiv(ae.get(), be); }); if (bei == bes.end()) return false; bei = bes.erase(bei); diff --git a/src/DPORDriver_test.h b/src/DPORDriver_test.h index 03be02480..30f9ae390 100644 --- a/src/DPORDriver_test.h +++ b/src/DPORDriver_test.h @@ -18,7 +18,6 @@ */ #include -#ifdef HAVE_BOOST_UNIT_TEST_FRAMEWORK #ifndef __DPOR_DRIVER_TEST_H__ #define __DPOR_DRIVER_TEST_H__ @@ -34,15 +33,6 @@ */ namespace DPORDriver_test { - /* A configuration with memory_model == TSO (resp. SC, PSO), and - * debug_collect_all_traces == true. - */ - const Configuration &get_tso_conf(); - const Configuration &get_sc_conf(); - const Configuration &get_pso_conf(); - const Configuration &get_power_conf(); - const Configuration &get_arm_conf(); - /* An IIDOrder object {a,b} should be interpreted as a predicate * over traces, with the meaning "a precedes b in time". */ @@ -64,6 +54,17 @@ namespace DPORDriver_test { */ typedef std::vector trace_set_spec; +#ifdef HAVE_BOOST_UNIT_TEST_FRAMEWORK + + /* A configuration with memory_model == TSO (resp. SC, PSO), and + * debug_collect_all_traces == true. + */ + const Configuration &get_tso_conf(); + const Configuration &get_sc_conf(); + const Configuration &get_pso_conf(); + const Configuration &get_power_conf(); + const Configuration &get_arm_conf(); + /* Returns the cross product of specs. */ trace_set_spec spec_xprod(const std::vector &specs); @@ -103,7 +104,7 @@ namespace DPORDriver_test { const DPORDriver::Result &optimal_res, const Configuration &conf); +#endif /* defined(HAVE_BOOST_UNIT_TEST_FRAMEWORK) */ } #endif -#endif diff --git a/src/Makefile.am b/src/Makefile.am index 04f93c8f3..9f3b89d63 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,6 +32,7 @@ libnidhugg_a_SOURCES = \ SymAddr.cpp SymAddr.h \ Trace.cpp Trace.h \ TraceBuilder.cpp TraceBuilder.h \ + TraceDumper.cpp TraceDumper.h \ Transform.cpp Transform.h \ TSOInterpreter.cpp TSOInterpreter.h \ TSOPSOTraceBuilder.h \ diff --git a/src/POWERARMTraceBuilder.cpp b/src/POWERARMTraceBuilder.cpp index 4f1e78518..ce92597b5 100644 --- a/src/POWERARMTraceBuilder.cpp +++ b/src/POWERARMTraceBuilder.cpp @@ -51,6 +51,11 @@ std::string PATB_impl::PATrace::to_string(int _ind) const{ return ss.str(); } +IID PATB_impl::PATrace::get_iid(int index) const{ + IID iiid = events[index].iid; + return {cpids[iiid.get_pid()], iiid.get_index()}; +} + std::string PATB_impl::TraceRecorder::to_string(int ind) const{ std::stringstream ss; for(const Line &L : lines){ @@ -94,11 +99,11 @@ bool PATB_impl::TraceRecorder::source_line(int proc, const llvm::MDNode *md, std if(md){ int lineno; std::string fname, dname; - if(get_location(md,&lineno,&fname,&dname)){ + if(Trace::get_location(md,&lineno,&fname,&dname)){ success = true; std::stringstream ss; - ss << basename(fname) << ":" << lineno - << " " << get_src_line_verbatim(md); + ss << Trace::basename(fname) << ":" << lineno + << " " << Trace::get_src_line_verbatim(md); ln += ss.str(); } } @@ -112,13 +117,15 @@ void PATB_impl::TraceRecorder::trace_register_metadata(int proc, const llvm::MDN std::string ln; int cpid_indent; source_line(proc,md,&ln,&cpid_indent); + std::string indent = ""; + for(int i = 0; i < cpid_indent; ++i) indent += " "; lines.push_back({proc,ln}); if(!last_committed.consumed){ last_committed.consumed = true; int load_count = 0; + event_descs[last_committed.iid] += ln; for(const Access &A : last_committed.accesses){ // One line per access ln = ""; - for(int i = 0; i < cpid_indent; ++i) ln += " "; // Indentation if(A.type == Access::LOAD){ ln += "load 0x"; std::stringstream ss; @@ -164,7 +171,8 @@ void PATB_impl::TraceRecorder::trace_register_metadata(int proc, const llvm::MDN << "]"; ln += ss.str(); } - lines.push_back({proc,ln}); + event_descs[last_committed.iid] += "\n" + ln; + lines.push_back({proc,indent+ln}); } } } diff --git a/src/POWERARMTraceBuilder.h b/src/POWERARMTraceBuilder.h index e5428821f..77b2c47b1 100644 --- a/src/POWERARMTraceBuilder.h +++ b/src/POWERARMTraceBuilder.h @@ -95,6 +95,8 @@ class POWERARMTraceBuilder : public TraceBuilder { /******************************** * Methods for Recording Traces * ********************************/ + /* Enables trace recording until the next reset. */ + virtual void enable_tracing() = 0; /* When recording a trace, trace_register_metadata(proc,md) should * be called by the ExecutionEngine when an instruction has been * committed. proc should be the committing thread and md should be diff --git a/src/POWERARMTraceBuilder.tcc b/src/POWERARMTraceBuilder.tcc index 16bc7641e..bb5dfc8e6 100644 --- a/src/POWERARMTraceBuilder.tcc +++ b/src/POWERARMTraceBuilder.tcc @@ -44,10 +44,10 @@ namespace PATB_impl{ template Trace *TB::get_trace() const{ - std::vector errs; + std::vector> errs; for(unsigned i = 0; i < errors.size(); ++i){ if(error_is_real(*errors[i])){ - errs.push_back(errors[i]->clone()); + errs.emplace_back(errors[i]->clone()); } } std::vector evts(prefix.size()); @@ -56,7 +56,8 @@ namespace PATB_impl{ evts[i].param.choices = get_evt(prefix[i]).cur_param.choices; // Ignore the rest of the parameter (relations etc.) } - return new PATrace(evts, cpids, conf, errs, TRec.to_string(2), + return new PATrace(evts, cpids, conf, std::move(errs), replay_point, + TRec.to_string(2), TRec.get_event_descs(), !sleepset_is_empty()); } @@ -2134,6 +2135,11 @@ namespace PATB_impl{ tgt = VecSet(std::move(AB)); } + template + void TB::enable_tracing(){ + TRec.activate(); + } + template void TB::trace_register_metadata(int proc, const llvm::MDNode *md){ if(TRec.is_active()){ @@ -2200,7 +2206,6 @@ namespace PATB_impl{ cpids.clear(); cpids.emplace_back(); CPS = CPidSystem(); - for(Error *e : errors) delete e; errors.clear(); TRec.clear(); TRec.activate(); @@ -2223,6 +2228,7 @@ namespace PATB_impl{ } } if(i < 0) return false; // Nothing more to explore. + replay_point = i; int new_pfx_len; Event &evt = get_evt(prefix[i]); @@ -2330,7 +2336,6 @@ namespace PATB_impl{ cpids.clear(); cpids.emplace_back(); CPS = CPidSystem(); - for(Error *e : errors) delete e; errors.clear(); TRec.clear(); TRec.deactivate(); diff --git a/src/POWERARMTraceBuilder_decl.h b/src/POWERARMTraceBuilder_decl.h index d06832873..80a54a965 100644 --- a/src/POWERARMTraceBuilder_decl.h +++ b/src/POWERARMTraceBuilder_decl.h @@ -567,11 +567,14 @@ namespace PATB_impl{ /* A TraceRecorder helps the TraceBuilder to record a human-readable * string representation of a trace. */ - class TraceRecorder : public Trace{ + class TraceRecorder{ public: - TraceRecorder(const std::vector *cpids) : Trace({}), cpids(cpids), active(false) {}; + TraceRecorder(const std::vector *cpids) : cpids(cpids), active(false) {}; /* Get the recorded trace representation. */ std::string to_string(int ind = 0) const; + const std::map,std::string> &get_event_descs() const { + return event_descs; + } /* Called when an event is committed by the TraceBuilder. */ void trace_commit(const IID &iid, const Param ¶m, const std::vector &accesses, const std::vector &baccesses, std::vector values); @@ -586,7 +589,12 @@ namespace PATB_impl{ /* Called when the ExecutionEngine detects an error. */ void trace_register_error(int proc, const std::string &err_msg); /* Clear the recorded trace */ - void clear() { lines.clear(); last_committed.consumed = true; fun_call_stack.clear(); }; + void clear() { + lines.clear(); + last_committed.consumed = true; + fun_call_stack.clear(); + event_descs.clear(); + }; void activate() { active = true; }; void deactivate() { active = false; }; bool is_active() const { return active; }; @@ -619,6 +627,11 @@ namespace PATB_impl{ }; std::vector lines; + /* Descriptions of the individual events in the execution. + * Contains a subset of the text in lines, in particular only + * the lines associated with a particular event. */ + std::map,std::string> event_descs; + /* Attempt to find the source code line associated with md. * * On success, true is returned. On success, *srcln is assigned a @@ -666,6 +679,7 @@ namespace PATB_impl{ virtual IID get_iid() const; virtual bool sleepset_is_empty() const; virtual void debug_print() const; + virtual void enable_tracing() override; virtual void trace_register_metadata(int proc, const llvm::MDNode *md); virtual void trace_register_external_function_call(int proc, const std::string &fname, const llvm::MDNode *md); virtual void trace_register_function_entry(int proc, const std::string &fname, const llvm::MDNode *md); @@ -1050,16 +1064,27 @@ namespace PATB_impl{ PATrace(const std::vector &events, const std::vector &cpids, const Configuration &conf, - const std::vector &errors, + std::vector> errors, + int replay_point, const std::string &str_rep = "", + std::map,std::string> event_descs = {}, bool blocked = false) - : Trace(errors,blocked), events(events), cpids(cpids), string_rep(str_rep), conf(conf) {}; + : Trace(std::move(errors),replay_point,blocked), events(events), + cpids(cpids), string_rep(str_rep), event_descs(event_descs), + conf(conf) {}; virtual ~PATrace(){}; virtual std::string to_string(int ind = 0) const; + virtual std::string event_desc(int event_index) const { + auto it = event_descs.find(events[event_index].iid); + return it != event_descs.end() ? it->second : ""; + } + virtual IID get_iid(int index) const override; + virtual std::size_t size() const override { return events.size(); } protected: std::vector events; std::vector cpids; std::string string_rep; + std::map,std::string> event_descs; Configuration conf; }; diff --git a/src/POWERInterpreter.cpp b/src/POWERInterpreter.cpp index 275ef72b8..b6d374abf 100644 --- a/src/POWERInterpreter.cpp +++ b/src/POWERInterpreter.cpp @@ -169,6 +169,10 @@ POWERInterpreter::runFunction(llvm::Function *F, for (unsigned i = 0; i < ArgCount; ++i) ActualArgs.push_back(ArgValues[i]); + if(conf.ee_store_trace){ + TB.enable_tracing(); + } + // Set up the function call. callFunction(F, ActualArgs); diff --git a/src/PSOTraceBuilder.cpp b/src/PSOTraceBuilder.cpp index 26cf1bef1..411c82aca 100644 --- a/src/PSOTraceBuilder.cpp +++ b/src/PSOTraceBuilder.cpp @@ -237,7 +237,7 @@ bool PSOTraceBuilder::check_for_cycles(){ { assert(prefix.size()); IID c_iid(threads[i_iid.get_pid()].cpid,i_iid.get_index()); - errors.push_back(new RobustnessError(c_iid)); + errors.emplace_back(new RobustnessError(c_iid)); } return true; @@ -246,15 +246,21 @@ bool PSOTraceBuilder::check_for_cycles(){ Trace *PSOTraceBuilder::get_trace() const{ std::vector > cmp; std::vector cmp_md; - std::vector errs; + std::vector > cmp_vc; + std::vector> errs; + std::vector cpids; + for (const Thread &t : threads){ + cpids.push_back(t.cpid); + } for(unsigned i = 0; i < prefix.size(); ++i){ - cmp.push_back(IID(threads[prefix[i].iid.get_pid()].cpid,prefix[i].iid.get_index())); + cmp.push_back(IID(cpids[prefix[i].iid.get_pid()],prefix[i].iid.get_index())); + cmp_vc.emplace_back(prefix[i].clock, cpids); cmp_md.push_back(prefix[i].md); } for(unsigned i = 0; i < errors.size(); ++i){ - errs.push_back(errors[i]->clone()); + errs.emplace_back(errors[i]->clone()); } - Trace *t = new IIDSeqTrace(cmp,cmp_md,errs); + Trace *t = new IIDVCSeqTrace(cmp,cmp_md,cmp_vc,std::move(errs),replay_point); t->set_blocked(!sleepset_is_empty()); return t; } @@ -277,6 +283,7 @@ bool PSOTraceBuilder::reset(){ /* No more branching is possible. */ return false; } + replay_point = i; /* Setup the new Event at prefix[i] */ { @@ -315,6 +322,7 @@ bool PSOTraceBuilder::reset(){ mutexes.clear(); cond_vars.clear(); mem.clear(); + errors.clear(); last_full_memory_conflict = -1; prefix_idx = -1; dryrun = false; diff --git a/src/TSOTraceBuilder.cpp b/src/TSOTraceBuilder.cpp index dad13e269..3e3b156ea 100644 --- a/src/TSOTraceBuilder.cpp +++ b/src/TSOTraceBuilder.cpp @@ -266,7 +266,7 @@ bool TSOTraceBuilder::check_for_cycles(){ { assert(prefix.len()); IID c_iid(threads[i_iid.get_pid()].cpid,i_iid.get_index()); - errors.push_back(new RobustnessError(c_iid)); + errors.emplace_back(new RobustnessError(c_iid)); } return true; @@ -275,15 +275,21 @@ bool TSOTraceBuilder::check_for_cycles(){ Trace *TSOTraceBuilder::get_trace() const{ std::vector > cmp; std::vector cmp_md; - std::vector errs; + std::vector > cmp_vc; + std::vector> errs; + std::vector cpids; + for (const Thread &t : threads){ + cpids.push_back(t.cpid); + } for(unsigned i = 0; i < prefix.len(); ++i){ - cmp.push_back(IID(threads[prefix[i].iid.get_pid()].cpid,prefix[i].iid.get_index())); + cmp.push_back(IID(cpids[prefix[i].iid.get_pid()],prefix[i].iid.get_index())); + cmp_vc.emplace_back(prefix[i].clock, cpids); cmp_md.push_back(prefix[i].md); }; for(unsigned i = 0; i < errors.size(); ++i){ - errs.push_back(errors[i]->clone()); + errs.emplace_back(errors[i]->clone()); } - Trace *t = new IIDSeqTrace(cmp,cmp_md,errs); + Trace *t = new IIDVCSeqTrace(cmp,cmp_md,cmp_vc,std::move(errs),replay_point); t->set_blocked(!sleepset_is_empty()); return t; } @@ -354,6 +360,7 @@ bool TSOTraceBuilder::reset(){ mutexes.clear(); cond_vars.clear(); mem.clear(); + errors.clear(); last_full_memory_conflict = -1; prefix_idx = -1; dryrun = false; diff --git a/src/TSO_test.cpp b/src/TSO_test.cpp index 2acb948e4..c64658d7d 100644 --- a/src/TSO_test.cpp +++ b/src/TSO_test.cpp @@ -39,7 +39,7 @@ define i32 @main(){ DPORDriver::Result res = driver->run(); BOOST_CHECK(res.all_traces.size() == 1); - BOOST_CHECK(static_cast(res.all_traces[0])->get_computation() == std::vector >({{CPid(),1}})); + BOOST_CHECK(static_cast(res.all_traces[0])->get_computation() == std::vector >({{CPid(),1}})); delete driver; @@ -48,7 +48,7 @@ define i32 @main(){ DPORDriver::Result opt_res = driver->run(); BOOST_CHECK(opt_res.all_traces.size() == 1); - BOOST_CHECK(static_cast(opt_res.all_traces[0])->get_computation() == std::vector >({{CPid(),1}})); + BOOST_CHECK(static_cast(opt_res.all_traces[0])->get_computation() == std::vector >({{CPid(),1}})); delete driver; BOOST_CHECK(DPORDriver_test::check_optimal_equiv(res, opt_res, conf)); diff --git a/src/Trace.cpp b/src/Trace.cpp index f18ce0649..364f93b5c 100644 --- a/src/Trace.cpp +++ b/src/Trace.cpp @@ -47,30 +47,30 @@ #include #endif -Trace::Trace(const std::vector &errors, bool blk) - : errors(errors), blocked(blk) { +Trace::Trace(std::vector> errors, int replay_point, bool blk) +: errors(std::move(errors)), blocked(blk), first_new_event(replay_point) { } Trace::~Trace(){ - for(unsigned i = 0; i < errors.size(); ++i){ - delete errors[i]; - } } -IIDSeqTrace::IIDSeqTrace(const std::vector > &cmp, - const std::vector &cmpmd, - const std::vector &errors, - bool blk) - : Trace(errors,blk), computation(cmp), computation_md(cmpmd) { +IIDVCSeqTrace::IIDVCSeqTrace(const std::vector > &cmp, + const std::vector &cmpmd, + const std::vector > &cmpvc, + std::vector> errors, + int replay_point, + bool blk) + : Trace(std::move(errors),replay_point,blk), computation(cmp), computation_md(cmpmd), + computation_clocks(cmpvc) { } -IIDSeqTrace::~IIDSeqTrace(){ +IIDVCSeqTrace::~IIDVCSeqTrace(){ } std::string Trace::to_string(int _ind) const{ if(errors.size()){ std::string s = "Errors found:\n"; - for(Error *e : errors){ + for(const std::unique_ptr &e : errors){ s += e->to_string()+"\n"; } return s; @@ -79,7 +79,28 @@ std::string Trace::to_string(int _ind) const{ } } -std::string IIDSeqTrace::to_string(int _ind) const{ +std::string IIDVCSeqTrace::event_desc(int i) const{ + std::string s = ""; + if(computation[i].get_pid().is_auxiliary()){ + s+=" UPDATE"; + } + { + int ln; + std::string fname, dname; + if(get_location(computation_md[i],&ln,&fname,&dname)){ + std::stringstream ss; + ss << " " << basename(fname) << ":" << ln; + std::string src_line = get_src_line_verbatim(computation_md[i]); + if (src_line != ""){ + ss << ": " << src_line; + } + s += ss.str(); + } + } + return s; +} + +std::string IIDVCSeqTrace::to_string(int _ind) const{ std::string s; std::string ind; assert(_ind >= 0); @@ -116,27 +137,15 @@ std::string IIDSeqTrace::to_string(int _ind) const{ } } assert(0 <= loc); - error_locs[loc] = errors[i]; + error_locs[loc] = errors[i].get(); } } for(unsigned i = 0; i < computation.size(); ++i){ std::string iid_str = ind + cpind[computation[i].get_pid()] + computation[i].to_string(); s += iid_str; - if(computation[i].get_pid().is_auxiliary()){ - s+=" UPDATE"; - } - { - int ln; - std::string fname, dname; - if(get_location(computation_md[i],&ln,&fname,&dname)){ - std::stringstream ss; - ss << " " << basename(fname) << ":" << ln; - ss << ": " << get_src_line_verbatim(computation_md[i]); - s += ss.str(); - } - s += "\n"; - } + s += event_desc(i); + s += "\n"; if(error_locs.count(i)){ // Indentation { diff --git a/src/Trace.h b/src/Trace.h index 01ce73e88..78f31faa6 100644 --- a/src/Trace.h +++ b/src/Trace.h @@ -29,6 +29,7 @@ #include "CPid.h" #include "IID.h" +#include "VClock.h" #include #include @@ -141,25 +142,34 @@ class Trace{ public: /* A Trace containing some errors. * - * This object takes ownership of errors. + * See replay_point() below for the meaning of replay_point. */ - Trace(const std::vector &errors, bool blocked = false); + Trace(std::vector> errors, int replay_point, + bool blocked = false); virtual ~Trace(); Trace(const Trace&) = delete; Trace &operator=(const Trace&) = delete; - /* The trace keeps ownership of the errors. */ - const std::vector &get_errors() const { return errors; }; + const std::vector> &get_errors() const { + return errors; + }; bool has_errors() const { return errors.size(); }; /* A multi-line, human-readable string representation of this * Trace. Indentation will be in multiples of ind spaces. */ virtual std::string to_string(int ind = 0) const; + /* Human-readable representation description of event, excluding IID. */ + virtual std::string event_desc(int event_index) const = 0; + /* IID of the event with index event_index. */ + virtual IID get_iid(int event_index) const = 0; + /* Numer of events in trace. */ + virtual std::size_t size() const = 0; + /* First event in this trace that differs from the previous trace; + * i.e. the point up until which the trace was replayed. + */ + int replay_point() const { return first_new_event; } /* Was the exploration of this execution (sleep set) blocked? */ virtual bool is_blocked() const { return blocked; }; virtual void set_blocked(bool b = true) { blocked = b; }; -protected: - std::vector errors; - bool blocked; /* Attempt to find the directory, file name and line number * corresponding to the metadata m. @@ -181,13 +191,17 @@ class Trace{ */ static std::string get_src_line_verbatim(const llvm::MDNode *m); static std::string basename(const std::string &fname); +protected: static bool is_absolute_path(const std::string &fname); + std::vector> errors; + bool blocked; + int first_new_event; }; /* This class represents traces that are expressed as sequences of - * IIDs. + * IIDs and VClocks. */ -class IIDSeqTrace : public Trace { +class IIDVCSeqTrace : public Trace { public: /* A Trace corresponding to the event sequence computation. * @@ -196,26 +210,36 @@ class IIDSeqTrace : public Trace { * (kind "dbg") for the event computation[i], or computation_md[i] * is null. * - * errors contains all errors that were discovered in this - * trace. This object takes ownership of errors. + * computation_clocks contains vector clocks corresponding to the + * events in computation, and represents the partial ordering of + * events that identify the equivalence class of traces that + * computation belongs to. */ - IIDSeqTrace(const std::vector > &computation, - const std::vector &computation_md, - const std::vector &errors, - bool blocked = false); - virtual ~IIDSeqTrace(); - IIDSeqTrace(const IIDSeqTrace&) = delete; - IIDSeqTrace &operator=(const IIDSeqTrace&) = delete; + IIDVCSeqTrace(const std::vector > &computation, + const std::vector &computation_md, + const std::vector > &computation_clocks, + std::vector> errors, + int replay_point, + bool blocked = false); + virtual ~IIDVCSeqTrace(); + IIDVCSeqTrace(const IIDVCSeqTrace&) = delete; + IIDVCSeqTrace &operator=(const IIDVCSeqTrace&) = delete; /* The sequence of events. */ virtual const std::vector > &get_computation() const { return computation; }; - /* The sequence of metadata (see above). */ - virtual const std::vector &get_computation_metadata() const{ - return computation_md; + /* The vector clock of the event with index event_index. */ + const VClock &get_clock(int event_index) const{ + return computation_clocks[event_index]; }; virtual std::string to_string(int ind = 0) const; + virtual std::string event_desc(int event_index) const; + virtual IID get_iid(int index) const override { + return computation[index]; + } + virtual std::size_t size() const override { return computation.size(); } protected: std::vector > computation; std::vector computation_md; + std::vector > computation_clocks; }; diff --git a/src/TraceBuilder.cpp b/src/TraceBuilder.cpp index f7f5c3f34..5f0a18a3b 100644 --- a/src/TraceBuilder.cpp +++ b/src/TraceBuilder.cpp @@ -19,56 +19,52 @@ #include "TraceBuilder.h" -TraceBuilder::TraceBuilder(const Configuration &C) : conf(C) { +TraceBuilder::TraceBuilder(const Configuration &C) : conf(C), replay_point(0) { } -TraceBuilder::~TraceBuilder(){ - for(unsigned i = 0; i < errors.size(); ++i){ - delete errors[i]; - } -} +TraceBuilder::~TraceBuilder() = default; void TraceBuilder::assertion_error(std::string cond, const IID &loc){ if(loc.is_null()){ - errors.push_back(new AssertionError(get_iid(),cond)); + errors.emplace_back(new AssertionError(get_iid(),cond)); }else{ - errors.push_back(new AssertionError(loc,cond)); + errors.emplace_back(new AssertionError(loc,cond)); } if(conf.debug_print_on_error) debug_print(); } void TraceBuilder::pthreads_error(std::string msg, const IID &loc){ if(loc.is_null()){ - errors.push_back(new PthreadsError(get_iid(),msg)); + errors.emplace_back(new PthreadsError(get_iid(),msg)); }else{ - errors.push_back(new PthreadsError(loc,msg)); + errors.emplace_back(new PthreadsError(loc,msg)); } if(conf.debug_print_on_error) debug_print(); } void TraceBuilder::segmentation_fault_error(const IID &loc){ if(loc.is_null()){ - errors.push_back(new SegmentationFaultError(get_iid())); + errors.emplace_back(new SegmentationFaultError(get_iid())); }else{ - errors.push_back(new SegmentationFaultError(loc)); + errors.emplace_back(new SegmentationFaultError(loc)); } if(conf.debug_print_on_error) debug_print(); } void TraceBuilder::memory_error(std::string msg, const IID &loc){ if(loc.is_null()){ - errors.push_back(new MemoryError(get_iid(),msg)); + errors.emplace_back(new MemoryError(get_iid(),msg)); }else{ - errors.push_back(new MemoryError(loc,msg)); + errors.emplace_back(new MemoryError(loc,msg)); } if(conf.debug_print_on_error) debug_print(); } void TraceBuilder::nondeterminism_error(std::string cond, const IID &loc){ if(loc.is_null()){ - errors.push_back(new NondeterminismError(get_iid(),cond)); + errors.emplace_back(new NondeterminismError(get_iid(),cond)); }else{ - errors.push_back(new NondeterminismError(loc,cond)); + errors.emplace_back(new NondeterminismError(loc,cond)); } if(conf.debug_print_on_error) debug_print(); } diff --git a/src/TraceBuilder.h b/src/TraceBuilder.h index d98a3a4af..9a358fe48 100644 --- a/src/TraceBuilder.h +++ b/src/TraceBuilder.h @@ -27,6 +27,7 @@ #include #include +#include /* A TraceBuilder object is maintained throughout DPOR trace * exploration. The TraceBuilder provides scheduling for each trace @@ -111,7 +112,9 @@ class TraceBuilder{ virtual int estimate_trace_count() const { return 1; }; protected: const Configuration &conf; - std::vector errors; + std::vector> errors; + /* The number of events that were replayed in the current computation. */ + int replay_point; }; #endif diff --git a/src/TraceDumper.cpp b/src/TraceDumper.cpp new file mode 100644 index 000000000..aa2c484ae --- /dev/null +++ b/src/TraceDumper.cpp @@ -0,0 +1,413 @@ +/* Copyright (C) 2017 Magnus Lång + * + * This file is part of Nidhugg. + * + * Nidhugg is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Nidhugg is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * . + */ + +#include "TraceDumper.h" + +#include "DPORDriver_test.h" + +#include +#include + +using DPORDriver_test::IIDOrder; +using DPORDriver_test::trace_spec; +using DPORDriver_test::trace_set_spec; + +namespace TraceDumper { + + static std::string dot_escape_string(const std::string &input) { + std::string output = ""; + for (char c : input) { + if (c == '\\') { + output += "\\\\"; + } else if (c == '"') { + output += "\\\""; + } else { + output += c; + } + } + return output; + } + + static std::string node_name(unsigned trace_index, const IID &iid) { + return "\"" + std::to_string(trace_index) + + " " + dot_escape_string(iid.to_string()) + "\""; + } + + static void print_trace_node + (std::fstream &out, unsigned ti, const Trace &trace, unsigned ei, + std::map,std::vector> &errorm) { + const IID &iid = trace.get_iid(ei); + out << " " << node_name(ti, iid) << " [label=\"" << iid + << dot_escape_string(trace.event_desc(ei)); + std::vector errors = std::move(errorm[trace.get_iid(ei)]); + errorm.erase(trace.get_iid(ei)); + for (const Error *error : errors) { + out << "\n" << dot_escape_string(error->to_string()); + } + out << "\","; + if (errors.size()) { + out << "color=red,penwidth=5,"; + } + out << "]\n"; + } + + static void print_end_node(std::fstream &out, unsigned ti, const Trace &t) { + out << " end_" << ti << " [label=\""; + if (t.has_errors()) { + out << "Error\",style=filled,fillcolor=red]\n"; + } else if (t.is_blocked()) { + out << "SSB\",style=filled,fillcolor=yellow]\n"; + } else { + out << "Ok\",style=filled,fillcolor=green]\n"; + } + } + + static VClock reconstruct_error_clock + (const IIDVCSeqTrace &t, const IID &iid, unsigned *last_index_out) { + VClock result = t.get_clock(*last_index_out = 0); + for (unsigned i = t.size()-1; i > 0; --i) { + if (t.get_iid(i).get_pid() == iid.get_pid()) { + result = t.get_clock(*last_index_out = i); + break; + } + } + result[iid.get_pid()] = iid.get_index(); + return result; + } + + static bool open_file(std::fstream &out, std::string filename) { + if (filename == "-") filename = "/dev/stdout"; + out.open(filename, std::fstream::out | std::fstream::trunc); + if (!out) { + throw std::logic_error("Failed to open output file " +filename);// << std::endl; + } + return !!out; + } + + static std::map,std::vector> sort_errors(const Trace &t) { + std::map,std::vector> errors; + for (const std::unique_ptr &error : t.get_errors()) { + errors[error->get_location()].push_back(error.get()); + } + return errors; + } + + static void dump_traces(const DPORDriver::Result &res, + const Configuration &conf) { + if (conf.memory_model != Configuration::SC + && conf.memory_model != Configuration::TSO + && conf.memory_model != Configuration::PSO) { + return; + } + std::fstream out; + if (!open_file(out, conf.trace_dump_file)) return; + out << "strict digraph {\n"; + out << " packmode=array_tuc1\n"; // Speed up layout + out << " node [shape=box,fontname=Monospace]\n"; + for (unsigned ti = 0; ti < res.all_traces.size(); ++ti) { + const IIDVCSeqTrace &t = static_cast(*res.all_traces[ti]); + std::map,std::vector> errors = sort_errors(t); + out << " subgraph trace_" << ti << " {\n"; + out << " sortv = " << ti << "\n"; + out << " start_" << ti << " [label=\"Trace " << (ti+1) << "\"]\n"; + out << " start_" << ti << " -> " << node_name(ti, t.get_iid(0)) << "\n"; + for (unsigned ei = 0; ei < t.size(); ++ei) { + print_trace_node(out, ti, t, ei, errors); + std::vector*> frontier; + for (unsigned pi = ei; pi > 0;) { + --pi; + if (t.get_clock(pi).leq(t.get_clock(ei)) + && !std::any_of(frontier.begin(), frontier.end(), + [&](const VClock *fc) { + return t.get_clock(pi).leq(*fc); + })) { + out << " " << node_name(ti, t.get_iid(pi)) << " -> " + << node_name(ti, t.get_iid(ei)) << "\n"; + frontier.push_back(&t.get_clock(pi)); + } + } + } + + std::vector> end_front; + for (std::pair,std::vector> &p : errors) { + out << " " << node_name(ti, p.first) + << " [color=red,penwidth=5,label=\""; + for (const Error *error : p.second) { + out << dot_escape_string(error->to_string()) << "\n"; + } + out << "\"]\n"; + out << " " << node_name(ti, p.first) << " -> " + << "end_" << ti << "\n"; + unsigned last_index; + end_front.push_back(reconstruct_error_clock(t, p.first, &last_index)); + out << " " << node_name(ti, t.get_iid(last_index)) << " -> " + << node_name(ti, p.first) << "\n"; + + } + /* Print end node */ + print_end_node(out, ti, t); + for (unsigned pi = t.size(); pi > 0;) { + --pi; + if (!std::any_of(end_front.begin(), end_front.end(), + [&](const VClock &fc) { + return t.get_clock(pi).leq(fc); + })) { + out << " " << node_name(ti, t.get_iid(pi)) << " -> " + << "end_" << ti << "\n"; + end_front.push_back(t.get_clock(pi)); + } + } + out << " }\n"; + } + + out << "}\n"; + } + + static void dump_tree(const DPORDriver::Result &res, + const Configuration &conf) { + std::fstream out; + if (!open_file(out, conf.tree_dump_file)) return; + out << "digraph {\n"; + out << " graph [ranksep=0.3]\n"; + out << " node [shape=box,width=7,fontname=Monospace]\n"; + out << " start [label=\"Initial\"]\n"; + + /* Labels of the events of the current column- */ + std::vector labels; + + for (unsigned ti = 0; ti < res.all_traces.size(); ++ti) { + const Trace &t = *res.all_traces[ti]; + std::map,std::vector> errors = sort_errors(t); + out << " subgraph trace_" << ti << " {\n"; + std::string first_node_name = node_name(ti, t.get_iid(t.replay_point())); + print_trace_node(out, ti, t, t.replay_point(), errors); + /* Edge from previous trace */ + if (labels.empty()) { + /* This is the first trace, edge from start node instead */ + out << " start -> " << first_node_name << " [weight=1000]\n"; + } else { + std::string previous_node_name = labels[t.replay_point()]; + std::string pre_prev_node_name = + t.replay_point() ? labels[t.replay_point() - 1] : "start"; + out << " " << pre_prev_node_name << " -> " << first_node_name + << " [style=invis,weight=1]\n"; + out << " " << previous_node_name << " -> " << first_node_name + << " [constraint=false]\n"; + labels.resize(t.replay_point()); + } + labels.push_back(first_node_name); + for (unsigned ei = t.replay_point()+1; ei < t.size(); ++ei) { + print_trace_node(out, ti, t, ei, errors); + out << " " << labels.back() << " -> " << node_name(ti, t.get_iid(ei)) + << " [weight=1000]\n"; + labels.push_back(node_name(ti, t.get_iid(ei))); + } + + for (std::pair,std::vector> &p : errors) { + out << " " << node_name(ti, p.first) + << " [color=red,penwidth=5,label=\""; + for (const Error *error : p.second) { + out << dot_escape_string(error->to_string()) << "\n"; + } + out << "\"]\n"; + out << " " << labels.back() << " -> " + << node_name(ti, p.first) << " [weight=1000]\n"; + labels.push_back(node_name(ti, p.first)); + } + /* Print end node */ + print_end_node(out, ti, t); + out << " " << labels.back() << " -> " << "end_" << ti + << " [weight=1000]\n"; + out << " }\n"; + } + + out << "}\n"; + } + + /* The transitive reduction of res */ + static trace_set_spec tred(const DPORDriver::Result &res) { + trace_set_spec set; + for (unsigned ti = 0; ti < res.all_traces.size(); ++ti) { + const IIDVCSeqTrace &t = static_cast(*res.all_traces[ti]); + if (t.is_blocked()) continue; + trace_spec spec; + for (unsigned ei = 0; ei < t.size(); ++ei) { + std::vector*> frontier; + for (unsigned pi = ei; pi > 0;) { + --pi; + if (t.get_clock(pi).leq(t.get_clock(ei)) + && !std::any_of(frontier.begin(), frontier.end(), + [&](const VClock *fc) { + return t.get_clock(pi).leq(*fc); + })) { + spec.push_back({t.get_iid(pi), t.get_iid(ei)}); + frontier.push_back(&t.get_clock(pi)); + } + } + } + set.emplace_back(std::move(spec)); + } + return set; + } + + static void delete_po(trace_set_spec &set) { + for (trace_spec &spec : set) { + for (auto it = spec.begin(); it != spec.end();) { + if (it->a.get_pid() == it->b.get_pid()) { + it = spec.erase(it); + } else { + ++it; + } + } + } + } + + static std::vector,const VClock*>> + get_traces(const DPORDriver::Result &res) { + std::vector,const VClock*>> traces; + for (const Trace *wtp : res.all_traces) { + const IIDVCSeqTrace &t = static_cast(*wtp); + if (t.is_blocked()) continue; + traces.emplace_back(); + for (unsigned ei = 0; ei < t.size(); ++ei) { + traces.back()[t.get_iid(ei)] = &t.get_clock(ei); + } + } + return traces; + } + + static void + filter_traces(std::vector,const VClock*>> &traces, + const IIDOrder &edge) { + for (auto it = traces.begin(); it != traces.end();) { + /* XXX: We check b !<= a rather than a <= b since + * DPORDriver_test::check_trace only checks that a was executed + * before b, and not that it actually *happens before* b. + */ + if (it->count(edge.a) && it->count(edge.b) + && !it->at(edge.b)->leq(*it->at(edge.a))) { + ++it; + } else { + it = traces.erase(it); + } + } + } + + /* Delete edges implied by all other edges in the set. + * + * Note: This is expensive and not guaranteed to find the global + * minumum. + */ + static void delete_implied(trace_set_spec &set, const DPORDriver::Result &res) { + for (trace_spec &spec : set) { + for (auto it = spec.end(); it != spec.begin();) { + --it; + auto traces = get_traces(res); + for (auto it2 = spec.begin(); it2 != spec.end(); ++it2) { + if (it2 != it) filter_traces(traces, *it2); + } + if (traces.size() == 1) { + it = spec.erase(it); + } + } + } + } + + static const std::string & + name_pid(std::fstream &out, std::map &map, + const CPid &cpid) { + if (map.count(cpid)) return map[cpid]; + CPid parent = cpid.parent(); + const std::string &parent_name = name_pid(out, map, parent); + std::string name = parent_name; + if (parent.has_parent()) name += "_"; + if (cpid.is_auxiliary()) { + name[0] = 'U'; + name += std::to_string(cpid.get_aux_index()); + out << "CPid " << name << " = " << parent_name << ".aux(" + << cpid.get_aux_index() << ");\n"; + } else { + name += std::to_string(cpid.get_child_index()); + out << "CPid " << name << " = " << parent_name << ".spawn(" + << cpid.get_child_index() << ");\n"; + } + map[cpid] = std::move(name); + return map[cpid]; + } + + static std::map + name_pids(std::fstream &out, const trace_set_spec &set) { + std::map map({{CPid(), "P"}}); + out << "CPid P;\n"; + for (const trace_spec &spec : set) { + for (const IIDOrder &edge : spec) { + name_pid(out, map, edge.a.get_pid()); + name_pid(out, map, edge.b.get_pid()); + } + } + return map; + } + + static void + write_spec_iid(std::fstream &out, const std::map &names, + const IID &iid) { + out << "{" << names.at(iid.get_pid()) << "," << iid.get_index() << "}"; + } + + static void dump_spec(const DPORDriver::Result &res, + const Configuration &conf) { + if (conf.memory_model != Configuration::SC + && conf.memory_model != Configuration::TSO + && conf.memory_model != Configuration::PSO) { + return; + } + std::fstream out; + if (!open_file(out, conf.spec_dump_file)) return; + + trace_set_spec set = tred(res); + delete_po(set); + delete_implied(set, res); + + std::map names = name_pids(out, set); + out << "DPORDriver_test::trace_set_spec expected = {\n"; + for (const trace_spec &spec : set) { + out << " {"; + for (auto it = spec.begin();;) { + out << "{"; + write_spec_iid(out, names, it->a); + out << ","; + write_spec_iid(out, names, it->b); + out << "}"; + if (++it == spec.end()) break; + out << ","; + } + out << "},\n"; + } + out << "};\n"; + } + + void dump(const DPORDriver::Result &res, + const Configuration &conf) { + if (!conf.debug_collect_all_traces) return; + if (!conf.trace_dump_file.empty()) dump_traces(res, conf); + if (!conf.tree_dump_file.empty()) dump_tree(res, conf); + if (!conf.spec_dump_file.empty()) dump_spec(res, conf); + } + +} diff --git a/src/TraceDumper.h b/src/TraceDumper.h new file mode 100644 index 000000000..bbd778ed9 --- /dev/null +++ b/src/TraceDumper.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2017 Magnus Lång + * + * This file is part of Nidhugg. + * + * Nidhugg is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Nidhugg is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see + * . + */ + +#include + +#include "Configuration.h" +#include "DPORDriver.h" + +#ifndef __TRACE_DUMPER_H__ +#define __TRACE_DUMPER_H__ + +namespace TraceDumper { + void dump(const DPORDriver::Result &res, const Configuration &conf); +} + +#endif diff --git a/src/main.cpp b/src/main.cpp index 86d191702..602df003b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ #include "DPORDriver.h" #include "GlobalContext.h" #include "Transform.h" +#include "TraceDumper.h" #include #include @@ -121,6 +122,8 @@ int main(int argc, char *argv[]){ std::cout << "No errors were detected." << std::endl; } + TraceDumper::dump(res,conf); + delete driver; } GlobalContext::destroy();