4987 - support `browse_trace` tool in SubX
I've extracted it into a separate binary, independent of my Mu prototype. I also cleaned up my tracing layer to be a little nicer. Major improvements: - Realized that incremental tracing really ought to be the default. And to minimize printing traces to screen. - Finally figured out how to combine layers and call stack frames in a single dimension of depth. The answer: optimize for the experience of `browse_trace`. Instructions occupy a range of depths based on their call stack frame, and minor details of an instruction lie one level deeper in each case. Other than that, I spent some time adjusting levels everywhere to make `browse_trace` useful.
This commit is contained in:
parent
e5998f74ac
commit
c442a5ad80
469
003trace.cc
469
003trace.cc
|
@ -13,30 +13,31 @@
|
|||
//: longer valid. In both cases you end up having to reorganize code as well as
|
||||
//: tests, an error-prone activity.
|
||||
//:
|
||||
//: In response, this layer introduces the notion of *domain-driven* testing.
|
||||
//: We focus on the domain of inputs the whole program needs to handle rather
|
||||
//: than the correctness of individual functions. All tests invoke the program
|
||||
//: in a single way: by calling run() with some input. As the program operates
|
||||
//: on the input, it traces out a list of _facts_ deduced about the domain:
|
||||
//: In response, this layer introduces the notion of domain-driven *white-box*
|
||||
//: testing. We focus on the domain of inputs the whole program needs to
|
||||
//: handle rather than the correctness of individual functions. All white-box
|
||||
//: tests (we call them 'scenarios') invoke the program in a single way: by
|
||||
//: calling run() with some input. As the program operates on the input, it
|
||||
//: traces out a list of _facts_ deduced about the domain:
|
||||
//: trace("label") << "fact 1: " << val;
|
||||
//:
|
||||
//: Tests can now check these facts:
|
||||
//: Scenarios can now check these facts:
|
||||
//: :(scenario foo)
|
||||
//: 34 # call run() with this input
|
||||
//: +label: fact 1: 34 # 'run' should have deduced this fact
|
||||
//: -label: fact 1: 35 # the trace should not contain such a fact
|
||||
//:
|
||||
//: Since we never call anything but the run() function directly, we never have
|
||||
//: to rewrite the tests when we reorganize the internals of the program. We
|
||||
//: to rewrite the scenarios when we reorganize the internals of the program. We
|
||||
//: just have to make sure our rewrite deduces the same facts about the domain,
|
||||
//: and that's something we're going to have to do anyway.
|
||||
//:
|
||||
//: To avoid the combinatorial explosion of integration tests, each layer
|
||||
//: mainly logs facts to the trace with a common *label*. All tests in a layer
|
||||
//: tend to check facts with this label. Validating the facts logged with a
|
||||
//: specific label is like calling functions of that layer directly.
|
||||
//: mainly logs facts to the trace with a common *label*. All scenarios in a
|
||||
//: layer tend to check facts with this label. Validating the facts logged
|
||||
//: with a specific label is like calling functions of that layer directly.
|
||||
//:
|
||||
//: To build robust tests, trace facts about your domain rather than details of
|
||||
//: To build robust scenarios, trace facts about your domain rather than details of
|
||||
//: how you computed them.
|
||||
//:
|
||||
//: More details: http://akkartik.name/blog/tracing-tests
|
||||
|
@ -44,159 +45,188 @@
|
|||
//: ---
|
||||
//:
|
||||
//: Between layers and domain-driven testing, programming starts to look like a
|
||||
//: fundamentally different activity. Instead of a) superficial, b) local rules
|
||||
//: on c) code [like say http://blog.bbv.ch/2013/06/05/clean-code-cheat-sheet],
|
||||
//: we allow programmers to engage with the a) deep, b) global structure of the
|
||||
//: c) domain. If you can systematically track discontinuities in the domain,
|
||||
//: you don't care if the code used gotos as long as it passed the tests. If
|
||||
//: tests become more robust to run it becomes easier to try out radically
|
||||
//: different implementations for the same program. If code is super-easy to
|
||||
//: rewrite, it becomes less important what indentation style it uses, or that
|
||||
//: the objects are appropriately encapsulated, or that the functions are
|
||||
//: referentially transparent.
|
||||
//: fundamentally different activity. Instead of focusing on a) superficial,
|
||||
//: b) local rules on c) code [like say http://blog.bbv.ch/2013/06/05/clean-code-cheat-sheet],
|
||||
//: we allow programmers to engage with the a) deep, b) global structure of
|
||||
//: the c) domain. If you can systematically track discontinuities in the
|
||||
//: domain, you don't care if the code used gotos as long as it passed all
|
||||
//: scenarios. If scenarios become more robust to run, it becomes easier to
|
||||
//: try out radically different implementations for the same program. If code
|
||||
//: is super-easy to rewrite, it becomes less important what indentation style
|
||||
//: it uses, or that the objects are appropriately encapsulated, or that the
|
||||
//: functions are referentially transparent.
|
||||
//:
|
||||
//: Instead of plumbing, programming becomes building and gradually refining a
|
||||
//: map of the environment the program must operate under. Whether a program is
|
||||
//: 'correct' at a given point in time is a red herring; what matters is
|
||||
//: avoiding regression by monotonically nailing down the more 'eventful' parts
|
||||
//: of the terrain. It helps readers new and old, and rewards curiosity, to
|
||||
//: organize large programs in self-similar hierarchies of example scenarios
|
||||
//: map of the environment the program must operate under. Whether a program
|
||||
//: is 'correct' at a given point in time is a red herring; what matters is
|
||||
//: avoiding regression by monotonically nailing down the more 'eventful'
|
||||
//: parts of the terrain. It helps readers new and old, and rewards curiosity,
|
||||
//: to organize large programs in self-similar hierarchies of example scenarios
|
||||
//: colocated with the code that makes them work.
|
||||
//:
|
||||
//: "Programming properly should be regarded as an activity by which
|
||||
//: programmers form a mental model, rather than as production of a program."
|
||||
//: -- Peter Naur (http://alistair.cockburn.us/ASD+book+extract%3A+%22Naur,+Ehn,+Musashi%22)
|
||||
|
||||
:(before "End Types")
|
||||
struct trace_line {
|
||||
int depth; // optional field just to help browse traces later
|
||||
string label;
|
||||
string contents;
|
||||
trace_line(string l, string c) :depth(0), label(l), contents(c) {}
|
||||
trace_line(int d, string l, string c) :depth(d), label(l), contents(c) {}
|
||||
};
|
||||
//:: == Core data structures
|
||||
|
||||
//: Support for tracing an entire run.
|
||||
//: Traces can have a lot of overhead, so only turn them on when asked.
|
||||
:(before "End Commandline Options(*arg)")
|
||||
else if (is_equal(*arg, "--trace")) {
|
||||
Save_trace = true;
|
||||
}
|
||||
:(before "End Commandline Parsing")
|
||||
if (Save_trace) {
|
||||
cerr << "initializing trace\n";
|
||||
Trace_stream = new trace_stream;
|
||||
}
|
||||
:(code)
|
||||
void cleanup_main() {
|
||||
if (!Trace_stream) return;
|
||||
if (Save_trace)
|
||||
Trace_stream->save();
|
||||
delete Trace_stream;
|
||||
Trace_stream = NULL;
|
||||
}
|
||||
:(before "End One-time Setup")
|
||||
atexit(cleanup_main);
|
||||
:(before "End Globals")
|
||||
trace_stream* Trace_stream = NULL;
|
||||
|
||||
:(before "End Types")
|
||||
// Pre-define some global constants that trace_stream needs to know about.
|
||||
// Since they're in the Types section, they'll be included in any cleaved
|
||||
// compilation units. So no extern linkage.
|
||||
const int Max_depth = 9999;
|
||||
const int Error_depth = 0; // definitely always print errors
|
||||
const int App_depth = 2; // temporarily where all Mu code will trace to
|
||||
|
||||
struct trace_stream {
|
||||
vector<trace_line> past_lines;
|
||||
// accumulator for current line
|
||||
ostringstream* curr_stream;
|
||||
string curr_label;
|
||||
int curr_depth;
|
||||
int callstack_depth;
|
||||
int collect_depth;
|
||||
ofstream null_stream; // never opens a file, so writes silently fail
|
||||
trace_stream() :curr_stream(NULL), curr_depth(Max_depth), callstack_depth(0), collect_depth(Max_depth) {}
|
||||
~trace_stream() { if (curr_stream) delete curr_stream; }
|
||||
// End trace_stream Fields
|
||||
|
||||
ostream& stream(string label) {
|
||||
return stream(Max_depth, label);
|
||||
trace_stream() {
|
||||
// End trace_stream Constructor
|
||||
}
|
||||
|
||||
ostream& stream(int depth, string label) {
|
||||
if (depth > collect_depth) return null_stream;
|
||||
curr_stream = new ostringstream;
|
||||
curr_label = label;
|
||||
curr_depth = depth;
|
||||
return *curr_stream;
|
||||
~trace_stream() {
|
||||
// End trace_stream Destructor
|
||||
}
|
||||
|
||||
void save() {
|
||||
cerr << "saving trace to 'last_run'\n";
|
||||
ofstream fout("last_run");
|
||||
fout << readable_contents("");
|
||||
fout.close();
|
||||
}
|
||||
|
||||
// be sure to call this before messing with curr_stream or curr_label
|
||||
void newline();
|
||||
// useful for debugging
|
||||
string readable_contents(string label); // empty label = show everything
|
||||
// End trace_stream Methods
|
||||
};
|
||||
|
||||
//:: == Adding to the trace
|
||||
|
||||
//: Top-level method is trace() which can be used like an ostream. Usage:
|
||||
//: trace(depth, label) << ... << end();
|
||||
//: Don't forget the 'end()' to actually append to the trace.
|
||||
:(before "End Includes")
|
||||
// No brackets around the expansion so that it prints nothing if Trace_stream
|
||||
// isn't initialized.
|
||||
#define trace(...) !Trace_stream ? cerr : Trace_stream->stream(__VA_ARGS__)
|
||||
|
||||
:(before "End trace_stream Fields")
|
||||
// accumulator for current trace_line
|
||||
ostringstream* curr_stream;
|
||||
string curr_label;
|
||||
int curr_depth;
|
||||
// other stuff
|
||||
int collect_depth; // avoid tracing lower levels for speed
|
||||
ofstream null_stream; // never opened, so writes to it silently fail
|
||||
|
||||
//: Some constants.
|
||||
:(before "struct trace_stream") // include constants in all cleaved compilation units
|
||||
const int Max_depth = 9999;
|
||||
// Most important traces are printed to the screen by default
|
||||
const int Error_depth = 0;
|
||||
:(before "End Globals")
|
||||
int Hide_errors = false; // if set, don't print errors to screen
|
||||
:(before "End trace_stream Constructor")
|
||||
curr_stream = NULL;
|
||||
curr_depth = Max_depth;
|
||||
collect_depth = Max_depth;
|
||||
:(before "End Reset")
|
||||
Hide_errors = false;
|
||||
|
||||
:(before "struct trace_stream")
|
||||
struct trace_line {
|
||||
string contents;
|
||||
string label;
|
||||
int depth; // 0 is 'sea level'; positive integers are progressively 'deeper' and lower level
|
||||
trace_line(string c, string l) {
|
||||
contents = c;
|
||||
label = l;
|
||||
depth = 0;
|
||||
}
|
||||
trace_line(string c, string l, int d) {
|
||||
contents = c;
|
||||
label = l;
|
||||
depth = d;
|
||||
}
|
||||
};
|
||||
|
||||
//: Starting a new trace line.
|
||||
:(before "End trace_stream Methods")
|
||||
ostream& stream(string label) {
|
||||
return stream(Max_depth, label);
|
||||
}
|
||||
|
||||
ostream& stream(int depth, string label) {
|
||||
if (depth > collect_depth) return null_stream;
|
||||
curr_stream = new ostringstream;
|
||||
curr_label = label;
|
||||
curr_depth = depth;
|
||||
return *curr_stream;
|
||||
}
|
||||
|
||||
//: End of a trace line; append it to the trace.
|
||||
:(before "End Types")
|
||||
struct end {};
|
||||
:(code)
|
||||
ostream& operator<<(ostream& os, end /*unused*/) {
|
||||
if (Trace_stream) Trace_stream->newline();
|
||||
return os;
|
||||
}
|
||||
|
||||
:(before "End trace_stream Methods")
|
||||
void newline();
|
||||
:(code)
|
||||
void trace_stream::newline() {
|
||||
if (!curr_stream) return;
|
||||
string curr_contents = curr_stream->str();
|
||||
if (!curr_contents.empty()) {
|
||||
past_lines.push_back(trace_line(curr_depth, trim(curr_label), curr_contents)); // preserve indent in contents
|
||||
if ((!Hide_errors && curr_label == "error")
|
||||
|| Dump_trace
|
||||
|| (!Dump_label.empty() && curr_label == Dump_label))
|
||||
cerr << curr_label << ": " << curr_contents << '\n';
|
||||
past_lines.push_back(trace_line(curr_contents, trim(curr_label), curr_depth)); // preserve indent in contents
|
||||
// maybe incrementally dump trace
|
||||
trace_line& t = past_lines.back();
|
||||
if (!Hide_errors && curr_depth == Error_depth) {
|
||||
cerr << std::setw(4) << t.depth << ' ' << t.label << ": " << t.contents << '\n';
|
||||
}
|
||||
// End trace Commit
|
||||
}
|
||||
|
||||
// clean up
|
||||
delete curr_stream;
|
||||
curr_stream = NULL;
|
||||
curr_label.clear();
|
||||
curr_depth = Max_depth;
|
||||
}
|
||||
|
||||
string trace_stream::readable_contents(string label) {
|
||||
ostringstream output;
|
||||
label = trim(label);
|
||||
for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p)
|
||||
if (label.empty() || label == p->label)
|
||||
output << std::setw(4) << p->depth << ' ' << p->label << ": " << p->contents << '\n';
|
||||
return output.str();
|
||||
}
|
||||
|
||||
:(before "End Globals")
|
||||
trace_stream* Trace_stream = NULL;
|
||||
int Trace_errors = 0; // used only when Trace_stream is NULL
|
||||
|
||||
:(before "End Globals")
|
||||
bool Hide_errors = false; // if set, don't print even error trace lines to screen
|
||||
bool Dump_trace = false; // if set, print trace lines to screen
|
||||
string Dump_label = ""; // if set, print trace lines matching a single label to screen
|
||||
:(before "End Reset")
|
||||
Hide_errors = false;
|
||||
Dump_trace = false; // toggle this to print traces to screen as they are emitted
|
||||
Dump_label = "";
|
||||
//:: == Initializing the trace in scenarios
|
||||
|
||||
:(before "End Includes")
|
||||
#define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream;
|
||||
#define START_TRACING_UNTIL_END_OF_SCOPE lease_tracer leased_tracer;
|
||||
:(before "End Test Setup")
|
||||
START_TRACING_UNTIL_END_OF_SCOPE
|
||||
|
||||
// Top-level helper. IMPORTANT: can't nest
|
||||
#define trace(...) !Trace_stream ? cerr /*print nothing*/ : Trace_stream->stream(__VA_ARGS__)
|
||||
//: Trace_stream is a resource, lease_tracer uses RAII to manage it.
|
||||
:(before "End Types")
|
||||
struct lease_tracer {
|
||||
lease_tracer();
|
||||
~lease_tracer();
|
||||
};
|
||||
:(code)
|
||||
lease_tracer::lease_tracer() { Trace_stream = new trace_stream; }
|
||||
lease_tracer::~lease_tracer() {
|
||||
delete Trace_stream;
|
||||
Trace_stream = NULL;
|
||||
}
|
||||
|
||||
// Just for debugging; 'git log' should never show any calls to 'dbg'.
|
||||
#define dbg trace(0, "a")
|
||||
#define DUMP(label) if (Trace_stream) cerr << Trace_stream->readable_contents(label);
|
||||
//:: == Errors using traces
|
||||
|
||||
// Errors are a special layer.
|
||||
:(before "End Includes")
|
||||
#define raise (!Trace_stream ? (scroll_to_bottom_and_close_console(),++Trace_errors,cerr) /*do print*/ : Trace_stream->stream(Error_depth, "error"))
|
||||
|
||||
:(before "End Globals")
|
||||
int Trace_errors = 0; // used only when Trace_stream is NULL
|
||||
|
||||
// Fail scenarios that displayed (unexpected) errors.
|
||||
// Expected errors should always be hidden and silently checked for.
|
||||
:(before "End Test Teardown")
|
||||
if (Passed && !Hide_errors && trace_contains_errors()) {
|
||||
Passed = false;
|
||||
}
|
||||
:(code)
|
||||
bool trace_contains_errors() {
|
||||
return Trace_errors > 0 || trace_count("error") > 0;
|
||||
}
|
||||
|
||||
:(before "End Includes")
|
||||
// If we aren't yet sure how to deal with some corner case, use assert_for_now
|
||||
// to indicate that it isn't an inviolable invariant.
|
||||
#define assert_for_now assert
|
||||
#define raise_for_now raise
|
||||
|
||||
//: Automatically close the console in some situations.
|
||||
:(before "End One-time Setup")
|
||||
|
@ -209,57 +239,30 @@ void scroll_to_bottom_and_close_console() {
|
|||
cout << "\r\n";
|
||||
tb_shutdown();
|
||||
}
|
||||
|
||||
// Inside tests, fail any tests that displayed (unexpected) errors.
|
||||
// Expected errors in tests should always be hidden and silently checked for.
|
||||
:(before "End Test Teardown")
|
||||
if (Passed && !Hide_errors && trace_contains_errors()) {
|
||||
Passed = false;
|
||||
}
|
||||
:(code)
|
||||
bool trace_contains_errors() {
|
||||
return Trace_errors > 0 || trace_count("error") > 0;
|
||||
}
|
||||
|
||||
:(before "End Types")
|
||||
struct end {};
|
||||
:(code)
|
||||
ostream& operator<<(ostream& os, end /*unused*/) {
|
||||
if (Trace_stream) Trace_stream->newline();
|
||||
return os;
|
||||
}
|
||||
|
||||
:(before "End Globals")
|
||||
bool Save_trace = false; // if set, write out trace to disk
|
||||
|
||||
// Trace_stream is a resource, lease_tracer uses RAII to manage it.
|
||||
:(before "End Types")
|
||||
struct lease_tracer {
|
||||
lease_tracer();
|
||||
~lease_tracer();
|
||||
};
|
||||
:(code)
|
||||
lease_tracer::lease_tracer() { Trace_stream = new trace_stream; }
|
||||
lease_tracer::~lease_tracer() {
|
||||
if (Save_trace) Trace_stream->save();
|
||||
delete Trace_stream, Trace_stream = NULL;
|
||||
}
|
||||
:(before "End Includes")
|
||||
#define START_TRACING_UNTIL_END_OF_SCOPE lease_tracer leased_tracer;
|
||||
:(before "End Test Setup")
|
||||
START_TRACING_UNTIL_END_OF_SCOPE
|
||||
#include "termbox/termbox.h"
|
||||
|
||||
//:: == Other assertions on traces
|
||||
//: Primitives:
|
||||
//: - CHECK_TRACE_CONTENTS(lines)
|
||||
//: Assert that the trace contains the given lines (separated by newlines)
|
||||
//: in order. There can be other intervening lines between them.
|
||||
//: - CHECK_TRACE_DOESNT_CONTAIN(line)
|
||||
//: - CHECK_TRACE_DOESNT_CONTAIN(label, contents)
|
||||
//: Assert that the trace doesn't contain the given (single) line.
|
||||
//: - CHECK_TRACE_COUNT(label, count)
|
||||
//: Assert that the trace contains exactly 'count' lines with the given
|
||||
//: 'label'.
|
||||
//: - CHECK_TRACE_CONTAINS_ERRORS()
|
||||
//: - CHECK_TRACE_DOESNT_CONTAIN_ERRORS()
|
||||
//: - trace_count_prefix(label, prefix)
|
||||
//: Count the number of trace lines with the given 'label' that start with
|
||||
//: the given 'prefix'.
|
||||
|
||||
:(before "End Includes")
|
||||
#define CHECK_TRACE_CONTENTS(...) check_trace_contents(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#define CHECK_TRACE_CONTAINS_ERRORS() CHECK(trace_contains_errors())
|
||||
#define CHECK_TRACE_DOESNT_CONTAIN_ERRORS() \
|
||||
if (Passed && trace_contains_errors()) { \
|
||||
cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \
|
||||
DUMP("error"); \
|
||||
Passed = false; \
|
||||
return; \
|
||||
}
|
||||
#define CHECK_TRACE_DOESNT_CONTAIN(...) CHECK(trace_doesnt_contain(__VA_ARGS__))
|
||||
|
||||
#define CHECK_TRACE_COUNT(label, count) \
|
||||
if (Passed && trace_count(label) != (count)) { \
|
||||
|
@ -270,7 +273,17 @@ START_TRACING_UNTIL_END_OF_SCOPE
|
|||
return; /* Currently we stop at the very first failure. */ \
|
||||
}
|
||||
|
||||
#define CHECK_TRACE_DOESNT_CONTAIN(...) CHECK(trace_doesnt_contain(__VA_ARGS__))
|
||||
#define CHECK_TRACE_CONTAINS_ERRORS() CHECK(trace_contains_errors())
|
||||
#define CHECK_TRACE_DOESNT_CONTAIN_ERRORS() \
|
||||
if (Passed && trace_contains_errors()) { \
|
||||
cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \
|
||||
DUMP("error"); \
|
||||
Passed = false; \
|
||||
return; \
|
||||
}
|
||||
|
||||
// Allow scenarios to ignore trace lines generated during setup.
|
||||
#define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream
|
||||
|
||||
:(code)
|
||||
bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expected) {
|
||||
|
@ -305,25 +318,13 @@ bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expecte
|
|||
return false;
|
||||
}
|
||||
|
||||
void split_label_contents(const string& s, string* label, string* contents) {
|
||||
static const string delim(": ");
|
||||
size_t pos = s.find(delim);
|
||||
if (pos == string::npos) {
|
||||
*label = "";
|
||||
*contents = trim(s);
|
||||
bool trace_doesnt_contain(string expected) {
|
||||
vector<string> tmp = split_first(expected, ": ");
|
||||
if (SIZE(tmp) == 1) {
|
||||
raise << expected << ": missing label or contents in trace line\n" << end();
|
||||
assert(false);
|
||||
}
|
||||
else {
|
||||
*label = trim(s.substr(0, pos));
|
||||
*contents = trim(s.substr(pos+SIZE(delim)));
|
||||
}
|
||||
}
|
||||
|
||||
bool line_exists_anywhere(const string& label, const string& contents) {
|
||||
for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
|
||||
if (label != p->label) continue;
|
||||
if (contents == trim(p->contents)) return true;
|
||||
}
|
||||
return false;
|
||||
return trace_count(tmp.at(0), tmp.at(1)) == 0;
|
||||
}
|
||||
|
||||
int trace_count(string label) {
|
||||
|
@ -354,17 +355,25 @@ int trace_count_prefix(string label, string prefix) {
|
|||
return result;
|
||||
}
|
||||
|
||||
bool trace_doesnt_contain(string label, string line) {
|
||||
return trace_count(label, line) == 0;
|
||||
void split_label_contents(const string& s, string* label, string* contents) {
|
||||
static const string delim(": ");
|
||||
size_t pos = s.find(delim);
|
||||
if (pos == string::npos) {
|
||||
*label = "";
|
||||
*contents = trim(s);
|
||||
}
|
||||
else {
|
||||
*label = trim(s.substr(0, pos));
|
||||
*contents = trim(s.substr(pos+SIZE(delim)));
|
||||
}
|
||||
}
|
||||
|
||||
bool trace_doesnt_contain(string expected) {
|
||||
vector<string> tmp = split_first(expected, ": ");
|
||||
if (SIZE(tmp) == 1) {
|
||||
raise << expected << ": missing label or contents in trace line\n" << end();
|
||||
assert(false);
|
||||
bool line_exists_anywhere(const string& label, const string& contents) {
|
||||
for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
|
||||
if (label != p->label) continue;
|
||||
if (contents == trim(p->contents)) return true;
|
||||
}
|
||||
return trace_doesnt_contain(tmp.at(0), tmp.at(1));
|
||||
return false;
|
||||
}
|
||||
|
||||
vector<string> split(string s, string delim) {
|
||||
|
@ -391,6 +400,54 @@ vector<string> split_first(string s, string delim) {
|
|||
return result;
|
||||
}
|
||||
|
||||
//:: == Helpers for debugging using traces
|
||||
|
||||
:(before "End Includes")
|
||||
// To debug why a scenario is failing, dump its trace using '?'.
|
||||
#define DUMP(label) if (Trace_stream) cerr << Trace_stream->readable_contents(label);
|
||||
|
||||
// To add temporary prints to the trace, use 'dbg'.
|
||||
// `git log` should never show any calls to 'dbg'.
|
||||
#define dbg trace(0, "a")
|
||||
|
||||
//: Dump the entire trace to file where it can be browsed offline.
|
||||
//: Dump the trace as it happens; that way you get something even if the
|
||||
//: program crashes.
|
||||
|
||||
:(before "End Globals")
|
||||
ofstream Trace_file;
|
||||
:(before "End Commandline Options(*arg)")
|
||||
else if (is_equal(*arg, "--trace")) {
|
||||
Trace_stream = new trace_stream;
|
||||
cerr << "saving trace to 'last_run'\n";
|
||||
Trace_file.open("last_run");
|
||||
}
|
||||
:(before "End trace Commit")
|
||||
if (Trace_file) {
|
||||
Trace_file << std::setw(4) << t.depth << ' ' << t.label << ": " << t.contents << '\n';
|
||||
}
|
||||
:(before "End One-time Setup")
|
||||
atexit(cleanup_main);
|
||||
:(code)
|
||||
void cleanup_main() {
|
||||
if (Trace_file) Trace_file.close();
|
||||
// End cleanup_main
|
||||
}
|
||||
|
||||
:(before "End trace_stream Methods")
|
||||
string readable_contents(string label) {
|
||||
string trim(const string& s); // prototype
|
||||
ostringstream output;
|
||||
label = trim(label);
|
||||
for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p)
|
||||
if (label.empty() || label == p->label)
|
||||
output << std::setw(4) << p->depth << ' ' << p->label << ": " << p->contents << '\n';
|
||||
return output.str();
|
||||
}
|
||||
|
||||
//: Miscellaneous helpers.
|
||||
|
||||
:(code)
|
||||
string trim(const string& s) {
|
||||
string::const_iterator first = s.begin();
|
||||
while (first != s.end() && isspace(*first))
|
||||
|
@ -419,19 +476,3 @@ using std::ostringstream;
|
|||
#include <fstream>
|
||||
using std::ifstream;
|
||||
using std::ofstream;
|
||||
|
||||
#include "termbox/termbox.h"
|
||||
|
||||
:(before "End Globals")
|
||||
//: In future layers we'll use the depth field as follows:
|
||||
//:
|
||||
//: Errors will be depth 0.
|
||||
//: Mu 'applications' will be able to use depths 1-100 as they like.
|
||||
//: Primitive statements will occupy 101-9989
|
||||
extern const int Initial_callstack_depth = 101;
|
||||
extern const int Max_callstack_depth = 9989;
|
||||
//: Finally, details of primitive Mu statements will occupy depth 9990-9999
|
||||
//: (more on that later as well)
|
||||
//:
|
||||
//: This framework should help us hide some details at each level, mixing
|
||||
//: static ideas like layers with the dynamic notion of call-stack depth.
|
||||
|
|
16
011load.cc
16
011load.cc
|
@ -63,13 +63,13 @@ int slurp_recipe(istream& in) {
|
|||
// End Recipe Refinements
|
||||
if (result.name.empty())
|
||||
raise << "empty result.name\n" << end();
|
||||
trace(9991, "parse") << "--- defining " << result.name << end();
|
||||
trace(101, "parse") << "--- defining " << result.name << end();
|
||||
if (!contains_key(Recipe_ordinal, result.name))
|
||||
put(Recipe_ordinal, result.name, Next_recipe_ordinal);
|
||||
result.ordinal = get(Recipe_ordinal, result.name);
|
||||
++Next_recipe_ordinal;
|
||||
if (Recipe.find(get(Recipe_ordinal, result.name)) != Recipe.end()) {
|
||||
trace(9991, "parse") << "already exists" << end();
|
||||
trace(101, "parse") << "already exists" << end();
|
||||
if (should_check_for_redefine(result.name))
|
||||
raise << "redefining recipe " << result.name << "\n" << end();
|
||||
Recipe.erase(get(Recipe_ordinal, result.name));
|
||||
|
@ -90,7 +90,7 @@ void slurp_body(istream& in, recipe& result) {
|
|||
while (next_instruction(in, &curr)) {
|
||||
curr.original_string = to_original_string(curr);
|
||||
// End Rewrite Instruction(curr, recipe result)
|
||||
trace(9992, "load") << "after rewriting: " << to_string(curr) << end();
|
||||
trace(102, "load") << "after rewriting: " << to_string(curr) << end();
|
||||
if (!curr.is_empty()) result.steps.push_back(curr);
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ bool next_instruction(istream& in, instruction* curr) {
|
|||
if (SIZE(words) == 1 && is_label_word(words.at(0))) {
|
||||
curr->is_label = true;
|
||||
curr->label = words.at(0);
|
||||
trace(9993, "parse") << "label: " << curr->label << end();
|
||||
trace(103, "parse") << "label: " << curr->label << end();
|
||||
if (!has_data(in)) {
|
||||
raise << "incomplete recipe at end of file (3)\n" << end();
|
||||
return false;
|
||||
|
@ -151,12 +151,12 @@ bool next_instruction(istream& in, instruction* curr) {
|
|||
for (; p != words.end(); ++p)
|
||||
curr->ingredients.push_back(reagent(*p));
|
||||
|
||||
trace(9993, "parse") << "instruction: " << curr->name << end();
|
||||
trace(9993, "parse") << " number of ingredients: " << SIZE(curr->ingredients) << end();
|
||||
trace(103, "parse") << "instruction: " << curr->name << end();
|
||||
trace(103, "parse") << " number of ingredients: " << SIZE(curr->ingredients) << end();
|
||||
for (vector<reagent>::iterator p = curr->ingredients.begin(); p != curr->ingredients.end(); ++p)
|
||||
trace(9993, "parse") << " ingredient: " << to_string(*p) << end();
|
||||
trace(103, "parse") << " ingredient: " << to_string(*p) << end();
|
||||
for (vector<reagent>::iterator p = curr->products.begin(); p != curr->products.end(); ++p)
|
||||
trace(9993, "parse") << " product: " << to_string(*p) << end();
|
||||
trace(103, "parse") << " product: " << to_string(*p) << end();
|
||||
if (!has_data(in)) {
|
||||
raise << "9: unbalanced '[' for recipe\n" << end();
|
||||
return false;
|
||||
|
|
|
@ -44,7 +44,7 @@ void initialize_transforms() {
|
|||
}
|
||||
|
||||
void transform_all() {
|
||||
trace(9990, "transform") << "=== transform_all()" << end();
|
||||
trace(100, "transform") << "=== transform_all()" << end();
|
||||
// Begin transform_all
|
||||
for (int t = 0; t < SIZE(Transform); ++t) {
|
||||
for (map<recipe_ordinal, recipe>::iterator p = Recipe.begin(); p != Recipe.end(); ++p) {
|
||||
|
@ -72,7 +72,7 @@ int Num_calls_to_transform_all = 0;
|
|||
|
||||
:(code)
|
||||
void parse_int_reagents() {
|
||||
trace(9991, "transform") << "--- parsing any uninitialized reagents as integers" << end();
|
||||
trace(101, "transform") << "--- parsing any uninitialized reagents as integers" << end();
|
||||
for (map<recipe_ordinal, recipe>::iterator p = Recipe.begin(); p != Recipe.end(); ++p) {
|
||||
recipe& r = p->second;
|
||||
if (r.steps.empty()) continue;
|
||||
|
|
|
@ -6,7 +6,7 @@ Transform.push_back(update_instruction_operations); // idempotent
|
|||
|
||||
:(code)
|
||||
void update_instruction_operations(const recipe_ordinal r) {
|
||||
trace(9991, "transform") << "--- compute instruction operations for recipe " << get(Recipe, r).name << end();
|
||||
trace(101, "transform") << "--- compute instruction operations for recipe " << get(Recipe, r).name << end();
|
||||
recipe& caller = get(Recipe, r);
|
||||
//? cerr << "--- compute instruction operations for recipe " << caller.name << '\n';
|
||||
for (int index = 0; index < SIZE(caller.steps); ++index) {
|
||||
|
|
|
@ -50,7 +50,7 @@ void load_type_abbreviations(istream& in) {
|
|||
raise << "'type' conflict: '" << new_type_name << "' defined as both '" << names_to_string_without_quotes(get(Type_abbreviations, new_type_name)) << "' and '" << old << "'\n" << end();
|
||||
return;
|
||||
}
|
||||
trace(9990, "type") << "alias " << new_type_name << " = " << old << end();
|
||||
trace(100, "type") << "alias " << new_type_name << " = " << old << end();
|
||||
type_tree* old_type = new_type_tree(old);
|
||||
put(Type_abbreviations, new_type_name, old_type);
|
||||
}
|
||||
|
@ -168,17 +168,17 @@ void expand_type_abbreviations(const recipe_ordinal r) {
|
|||
}
|
||||
|
||||
void expand_type_abbreviations(const recipe& caller) {
|
||||
trace(9991, "transform") << "--- expand type abbreviations in recipe '" << caller.name << "'" << end();
|
||||
trace(101, "transform") << "--- expand type abbreviations in recipe '" << caller.name << "'" << end();
|
||||
for (int i = 0; i < SIZE(caller.steps); ++i) {
|
||||
const instruction& inst = caller.steps.at(i);
|
||||
trace(9991, "transform") << "instruction '" << to_original_string(inst) << end();
|
||||
trace(102, "transform") << "instruction '" << to_original_string(inst) << end();
|
||||
for (long int i = 0; i < SIZE(inst.ingredients); ++i) {
|
||||
expand_type_abbreviations(inst.ingredients.at(i).type);
|
||||
trace(9992, "transform") << "ingredient type after expanding abbreviations: " << names_to_string(inst.ingredients.at(i).type) << end();
|
||||
trace(102, "transform") << "ingredient type after expanding abbreviations: " << names_to_string(inst.ingredients.at(i).type) << end();
|
||||
}
|
||||
for (long int i = 0; i < SIZE(inst.products); ++i) {
|
||||
expand_type_abbreviations(inst.products.at(i).type);
|
||||
trace(9992, "transform") << "product type after expanding abbreviations: " << names_to_string(inst.products.at(i).type) << end();
|
||||
trace(102, "transform") << "product type after expanding abbreviations: " << names_to_string(inst.products.at(i).type) << end();
|
||||
}
|
||||
}
|
||||
// End Expand Type Abbreviations(caller)
|
||||
|
|
33
020run.cc
33
020run.cc
|
@ -63,7 +63,7 @@ void run_current_routine() {
|
|||
while (should_continue_running(Current_routine)) { // beware: may modify Current_routine
|
||||
// Running One Instruction
|
||||
if (current_instruction().is_label) { ++current_step_index(); continue; }
|
||||
trace(Initial_callstack_depth + Trace_stream->callstack_depth, "run") << to_string(current_instruction()) << end();
|
||||
trace(Callstack_depth, "run") << to_string(current_instruction()) << end();
|
||||
//? if (Foo) cerr << "run: " << to_string(current_instruction()) << '\n';
|
||||
if (get_or_insert(Memory, 0) != 0) {
|
||||
raise << "something wrote to location 0; this should never happen\n" << end();
|
||||
|
@ -113,6 +113,26 @@ void run_current_routine() {
|
|||
stop_running_current_routine:;
|
||||
}
|
||||
|
||||
//: Helpers for managing trace depths
|
||||
//:
|
||||
//: We're going to use trace depths primarily to segment code running at
|
||||
//: different frames of the call stack. This will make it easy for the trace
|
||||
//: browser to collapse over entire calls.
|
||||
//:
|
||||
//: The entire map of possible depths is as follows:
|
||||
//:
|
||||
//: Errors will be depth 0.
|
||||
//: Mu 'applications' will be able to use depths 1-99 as they like.
|
||||
//: Primitive statements will occupy 100 and up to Max_depth, organized by
|
||||
//: stack frames.
|
||||
:(before "End Globals")
|
||||
extern const int Initial_callstack_depth = 100;
|
||||
int Callstack_depth = Initial_callstack_depth;
|
||||
:(before "End Reset")
|
||||
Callstack_depth = Initial_callstack_depth;
|
||||
|
||||
//: Other helpers for the VM.
|
||||
|
||||
:(code)
|
||||
//: hook replaced in a later layer
|
||||
bool should_continue_running(const routine* current_routine) {
|
||||
|
@ -255,7 +275,7 @@ void load_file_or_directory(string filename) {
|
|||
cerr << "no such file '" << filename << "'\n" << end(); // don't raise, just warn. just in case it's just a name for a scenario to run.
|
||||
return;
|
||||
}
|
||||
trace(9990, "load") << "=== " << filename << end();
|
||||
trace(2, "load") << "=== " << filename << end();
|
||||
load(fin);
|
||||
fin.close();
|
||||
}
|
||||
|
@ -306,7 +326,7 @@ vector<double> read_memory(reagent/*copy*/ x) {
|
|||
int size = size_of(x);
|
||||
for (int offset = 0; offset < size; ++offset) {
|
||||
double val = get_or_insert(Memory, x.value+offset);
|
||||
trace("mem") << "location " << x.value+offset << " is " << no_scientific(val) << end();
|
||||
trace(Callstack_depth+1, "mem") << "location " << x.value+offset << " is " << no_scientific(val) << end();
|
||||
result.push_back(val);
|
||||
}
|
||||
return result;
|
||||
|
@ -333,7 +353,7 @@ void write_memory(reagent/*copy*/ x, const vector<double>& data) {
|
|||
// End write_memory(x) Special-cases
|
||||
for (int offset = 0; offset < SIZE(data); ++offset) {
|
||||
assert(x.value+offset > 0);
|
||||
trace("mem") << "storing " << no_scientific(data.at(offset)) << " in location " << x.value+offset << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing " << no_scientific(data.at(offset)) << " in location " << x.value+offset << end();
|
||||
//? if (Foo) cerr << "mem: storing " << no_scientific(data.at(offset)) << " in location " << x.value+offset << '\n';
|
||||
put(Memory, x.value+offset, data.at(offset));
|
||||
}
|
||||
|
@ -392,10 +412,7 @@ void run(const string& form) {
|
|||
vector<recipe_ordinal> tmp = load(form);
|
||||
transform_all();
|
||||
if (tmp.empty()) return;
|
||||
if (trace_contains_errors()) {
|
||||
if (Save_trace && Trace_stream) Trace_stream->save();
|
||||
return;
|
||||
}
|
||||
if (trace_contains_errors()) return;
|
||||
// if a test defines main, it probably wants to start there regardless of
|
||||
// definition order
|
||||
if (contains_key(Recipe, get(Recipe_ordinal, "main")))
|
||||
|
|
|
@ -13,7 +13,7 @@ Transform.push_back(check_instruction); // idempotent
|
|||
|
||||
:(code)
|
||||
void check_instruction(const recipe_ordinal r) {
|
||||
trace(9991, "transform") << "--- perform checks for recipe " << get(Recipe, r).name << end();
|
||||
trace(101, "transform") << "--- perform checks for recipe " << get(Recipe, r).name << end();
|
||||
map<string, vector<type_ordinal> > metadata;
|
||||
for (int i = 0; i < SIZE(get(Recipe, r).steps); ++i) {
|
||||
instruction& inst = get(Recipe, r).steps.at(i);
|
||||
|
|
10
024jump.cc
10
024jump.cc
|
@ -33,7 +33,7 @@ case JUMP: {
|
|||
case JUMP: {
|
||||
assert(current_instruction().ingredients.at(0).initialized);
|
||||
current_step_index() += ingredients.at(0).at(0)+1;
|
||||
trace(9998, "run") << "jumping to instruction " << current_step_index() << end();
|
||||
trace(Callstack_depth+1, "run") << "jumping to instruction " << current_step_index() << end();
|
||||
// skip rest of this instruction
|
||||
write_products = false;
|
||||
fall_through_to_next_instruction = false;
|
||||
|
@ -91,11 +91,11 @@ case JUMP_IF: {
|
|||
case JUMP_IF: {
|
||||
assert(current_instruction().ingredients.at(1).initialized);
|
||||
if (!scalar_ingredient(ingredients, 0)) {
|
||||
trace(9998, "run") << "jump-if fell through" << end();
|
||||
trace(Callstack_depth+1, "run") << "jump-if fell through" << end();
|
||||
break;
|
||||
}
|
||||
current_step_index() += ingredients.at(1).at(0)+1;
|
||||
trace(9998, "run") << "jumping to instruction " << current_step_index() << end();
|
||||
trace(Callstack_depth+1, "run") << "jumping to instruction " << current_step_index() << end();
|
||||
// skip rest of this instruction
|
||||
write_products = false;
|
||||
fall_through_to_next_instruction = false;
|
||||
|
@ -162,11 +162,11 @@ case JUMP_UNLESS: {
|
|||
case JUMP_UNLESS: {
|
||||
assert(current_instruction().ingredients.at(1).initialized);
|
||||
if (scalar_ingredient(ingredients, 0)) {
|
||||
trace(9998, "run") << "jump-unless fell through" << end();
|
||||
trace(Callstack_depth+1, "run") << "jump-unless fell through" << end();
|
||||
break;
|
||||
}
|
||||
current_step_index() += ingredients.at(1).at(0)+1;
|
||||
trace(9998, "run") << "jumping to instruction " << current_step_index() << end();
|
||||
trace(Callstack_depth+1, "run") << "jumping to instruction " << current_step_index() << end();
|
||||
// skip rest of this instruction
|
||||
write_products = false;
|
||||
fall_through_to_next_instruction = false;
|
||||
|
|
24
026call.cc
24
026call.cc
|
@ -60,11 +60,9 @@ struct routine {
|
|||
};
|
||||
:(code)
|
||||
routine::routine(recipe_ordinal r) {
|
||||
if (Trace_stream) {
|
||||
++Trace_stream->callstack_depth;
|
||||
trace("trace") << "new routine; incrementing callstack depth to " << Trace_stream->callstack_depth << end();
|
||||
assert(Trace_stream->callstack_depth < 9000); // 9998-101 plus cushion
|
||||
}
|
||||
++Callstack_depth;
|
||||
trace(Callstack_depth+1, "trace") << "new routine; incrementing callstack depth to " << Callstack_depth << end();
|
||||
assert(Callstack_depth < Max_depth);
|
||||
calls.push_front(call(r));
|
||||
// End routine Constructor
|
||||
}
|
||||
|
@ -151,11 +149,9 @@ if (!contains_key(Recipe, inst.operation)) {
|
|||
default: {
|
||||
if (contains_key(Recipe, current_instruction().operation)) { // error already raised in Checks above
|
||||
// not a primitive; look up the book of recipes
|
||||
if (Trace_stream) {
|
||||
++Trace_stream->callstack_depth;
|
||||
trace("trace") << "incrementing callstack depth to " << Trace_stream->callstack_depth << end();
|
||||
assert(Trace_stream->callstack_depth < 9000); // 9998-101 plus cushion
|
||||
}
|
||||
++Callstack_depth;
|
||||
trace(Callstack_depth+1, "trace") << "incrementing callstack depth to " << Callstack_depth << end();
|
||||
assert(Callstack_depth < Max_depth);
|
||||
const call& caller_frame = current_call();
|
||||
Current_routine->calls.push_front(call(to_instruction(caller_frame).operation));
|
||||
finish_call_housekeeping(to_instruction(caller_frame), ingredients);
|
||||
|
@ -202,11 +198,9 @@ const vector<instruction>& routine::steps() const {
|
|||
// it, and the one below that, and so on
|
||||
while (current_step_index() >= SIZE(Current_routine->steps())) {
|
||||
// Falling Through End Of Recipe
|
||||
if (Trace_stream) {
|
||||
trace("trace") << "fall-through: exiting " << current_recipe_name() << "; decrementing callstack depth from " << Trace_stream->callstack_depth << end();
|
||||
--Trace_stream->callstack_depth;
|
||||
assert(Trace_stream->callstack_depth >= 0);
|
||||
}
|
||||
trace(Callstack_depth+1, "trace") << "fall-through: exiting " << current_recipe_name() << "; decrementing callstack depth from " << Callstack_depth << end();
|
||||
--Callstack_depth;
|
||||
assert(Callstack_depth >= 0);
|
||||
Current_routine->calls.pop_front();
|
||||
if (Current_routine->calls.empty()) goto stop_running_current_routine;
|
||||
// Complete Call Fallthrough
|
||||
|
|
|
@ -37,19 +37,17 @@ case RETURN: {
|
|||
:(before "End Primitive Recipe Implementations")
|
||||
case RETURN: {
|
||||
// Begin Return
|
||||
if (Trace_stream) {
|
||||
trace("trace") << current_instruction().name << ": decrementing callstack depth from " << Trace_stream->callstack_depth << end();
|
||||
--Trace_stream->callstack_depth;
|
||||
if (Trace_stream->callstack_depth < 0) {
|
||||
Current_routine->calls.clear();
|
||||
goto stop_running_current_routine;
|
||||
}
|
||||
trace(Callstack_depth+1, "trace") << current_instruction().name << ": decrementing callstack depth from " << Callstack_depth << end();
|
||||
--Callstack_depth;
|
||||
if (Callstack_depth < 0) {
|
||||
Current_routine->calls.clear();
|
||||
goto stop_running_current_routine;
|
||||
}
|
||||
Current_routine->calls.pop_front();
|
||||
// just in case 'main' returns a value, drop it for now
|
||||
if (Current_routine->calls.empty()) goto stop_running_current_routine;
|
||||
for (int i = 0; i < SIZE(ingredients); ++i)
|
||||
trace(9998, "run") << "result " << i << " is " << to_string(ingredients.at(i)) << end();
|
||||
trace(Callstack_depth+1, "run") << "result " << i << " is " << to_string(ingredients.at(i)) << end();
|
||||
// make return products available to caller
|
||||
copy(ingredients.begin(), ingredients.end(), inserter(products, products.begin()));
|
||||
// End Return
|
||||
|
|
18
029tools.cc
18
029tools.cc
|
@ -169,20 +169,6 @@ case _CLEAR_TRACE: {
|
|||
break;
|
||||
}
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
_SAVE_TRACE,
|
||||
:(before "End Primitive Recipe Numbers")
|
||||
put(Recipe_ordinal, "$save-trace", _SAVE_TRACE);
|
||||
:(before "End Primitive Recipe Checks")
|
||||
case _SAVE_TRACE: {
|
||||
break;
|
||||
}
|
||||
:(before "End Primitive Recipe Implementations")
|
||||
case _SAVE_TRACE: {
|
||||
if (Save_trace) Trace_stream->save();
|
||||
break;
|
||||
}
|
||||
|
||||
//:: 'cheating' by using the host system
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
|
@ -197,7 +183,7 @@ case _PRINT: {
|
|||
case _PRINT: {
|
||||
for (int i = 0; i < SIZE(ingredients); ++i) {
|
||||
if (is_literal(current_instruction().ingredients.at(i))) {
|
||||
trace(9998, "run") << "$print: " << current_instruction().ingredients.at(i).name << end();
|
||||
trace(Callstack_depth+1, "run") << "$print: " << current_instruction().ingredients.at(i).name << end();
|
||||
if (!has_property(current_instruction().ingredients.at(i), "newline")) {
|
||||
cout << current_instruction().ingredients.at(i).name;
|
||||
}
|
||||
|
@ -210,7 +196,7 @@ case _PRINT: {
|
|||
// End $print Special-cases
|
||||
else {
|
||||
for (int j = 0; j < SIZE(ingredients.at(i)); ++j) {
|
||||
trace(9998, "run") << "$print: " << ingredients.at(i).at(j) << end();
|
||||
trace(Callstack_depth+1, "run") << "$print: " << ingredients.at(i).at(j) << end();
|
||||
if (j > 0) cout << " ";
|
||||
cout << no_scientific(ingredients.at(i).at(j));
|
||||
}
|
||||
|
|
|
@ -198,11 +198,11 @@ case GET: {
|
|||
int src = base_address;
|
||||
for (int i = 0; i < offset; ++i)
|
||||
src += size_of(element_type(base.type, i));
|
||||
trace(9998, "run") << "address to copy is " << src << end();
|
||||
trace(Callstack_depth+1, "run") << "address to copy is " << src << end();
|
||||
//: use base.type rather than base_type because later layers will introduce compound types
|
||||
reagent/*copy*/ element = element_type(base.type, offset);
|
||||
element.set_value(src);
|
||||
trace(9998, "run") << "its type is " << names_to_string(element.type) << end();
|
||||
trace(Callstack_depth+1, "run") << "its type is " << names_to_string(element.type) << end();
|
||||
// Read element
|
||||
products.push_back(read_memory(element));
|
||||
break;
|
||||
|
@ -355,13 +355,13 @@ case PUT: {
|
|||
int address = base_address;
|
||||
for (int i = 0; i < offset; ++i)
|
||||
address += size_of(element_type(base.type, i));
|
||||
trace(9998, "run") << "address to copy to is " << address << end();
|
||||
trace(Callstack_depth+1, "run") << "address to copy to is " << address << end();
|
||||
// optimization: directly write the element rather than updating 'product'
|
||||
// and writing the entire container
|
||||
// Write Memory in PUT in Run
|
||||
write_products = false;
|
||||
for (int i = 0; i < SIZE(ingredients.at(2)); ++i) {
|
||||
trace("mem") << "storing " << no_scientific(ingredients.at(2).at(i)) << " in location " << address+i << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing " << no_scientific(ingredients.at(2).at(i)) << " in location " << address+i << end();
|
||||
put(Memory, address+i, ingredients.at(2).at(i));
|
||||
}
|
||||
break;
|
||||
|
@ -452,12 +452,12 @@ void insert_container(const string& command, kind_of_type kind, istream& in) {
|
|||
return;
|
||||
}
|
||||
// End container Name Refinements
|
||||
trace(9991, "parse") << "--- defining " << command << ' ' << name << end();
|
||||
trace(101, "parse") << "--- defining " << command << ' ' << name << end();
|
||||
if (!contains_key(Type_ordinal, name)
|
||||
|| get(Type_ordinal, name) == 0) {
|
||||
put(Type_ordinal, name, Next_type_ordinal++);
|
||||
}
|
||||
trace("parse") << "type number: " << get(Type_ordinal, name) << end();
|
||||
trace(102, "parse") << "type number: " << get(Type_ordinal, name) << end();
|
||||
skip_bracket(in, "'"+command+"' must begin with '['");
|
||||
type_info& info = get_or_insert(Type, get(Type_ordinal, name));
|
||||
if (info.Num_calls_to_transform_all_at_first_definition == -1) {
|
||||
|
@ -492,7 +492,7 @@ void insert_container(const string& command, kind_of_type kind, istream& in) {
|
|||
info.elements.push_back(reagent(element));
|
||||
expand_type_abbreviations(info.elements.back().type); // todo: use abbreviation before declaration
|
||||
replace_unknown_types_with_unique_ordinals(info.elements.back().type, info);
|
||||
trace(9993, "parse") << " element: " << to_string(info.elements.back()) << end();
|
||||
trace(103, "parse") << " element: " << to_string(info.elements.back()) << end();
|
||||
// End Load Container Element Definition
|
||||
}
|
||||
}
|
||||
|
@ -602,7 +602,7 @@ Transform.push_back(check_or_set_invalid_types); // idempotent
|
|||
:(code)
|
||||
void check_or_set_invalid_types(const recipe_ordinal r) {
|
||||
recipe& caller = get(Recipe, r);
|
||||
trace(9991, "transform") << "--- check for invalid types in recipe " << caller.name << end();
|
||||
trace(101, "transform") << "--- check for invalid types in recipe " << caller.name << end();
|
||||
for (int index = 0; index < SIZE(caller.steps); ++index) {
|
||||
instruction& inst = caller.steps.at(index);
|
||||
for (int i = 0; i < SIZE(inst.ingredients); ++i)
|
||||
|
|
|
@ -114,7 +114,7 @@ Transform.push_back(check_merge_calls); // idempotent
|
|||
:(code)
|
||||
void check_merge_calls(const recipe_ordinal r) {
|
||||
const recipe& caller = get(Recipe, r);
|
||||
trace(9991, "transform") << "--- type-check merge instructions in recipe " << caller.name << end();
|
||||
trace(101, "transform") << "--- type-check merge instructions in recipe " << caller.name << end();
|
||||
for (int i = 0; i < SIZE(caller.steps); ++i) {
|
||||
const instruction& inst = caller.steps.at(i);
|
||||
if (inst.name != "merge") continue;
|
||||
|
@ -145,7 +145,7 @@ void check_merge_call(const vector<reagent>& ingredients, const reagent& product
|
|||
state.data.push(merge_check_point(product, 0));
|
||||
while (true) {
|
||||
assert(!state.data.empty());
|
||||
trace("transform") << ingredient_index << " vs " << SIZE(ingredients) << end();
|
||||
trace(102, "transform") << ingredient_index << " vs " << SIZE(ingredients) << end();
|
||||
if (ingredient_index >= SIZE(ingredients)) {
|
||||
raise << maybe(caller.name) << "too few ingredients in '" << to_original_string(inst) << "'\n" << end();
|
||||
return;
|
||||
|
@ -161,7 +161,7 @@ void check_merge_call(const vector<reagent>& ingredients, const reagent& product
|
|||
if (state.data.top().container_element_index == 0 && types_coercible(container, inst.ingredients.at(ingredient_index)))
|
||||
return;
|
||||
const reagent& expected_ingredient = element_type(container.type, state.data.top().container_element_index);
|
||||
trace("transform") << "checking container " << to_string(container) << " || " << to_string(expected_ingredient) << " vs ingredient " << ingredient_index << end();
|
||||
trace(102, "transform") << "checking container " << to_string(container) << " || " << to_string(expected_ingredient) << " vs ingredient " << ingredient_index << end();
|
||||
// if the current element is the ingredient we expect, move on to the next element/ingredient
|
||||
if (types_coercible(expected_ingredient, ingredients.at(ingredient_index))) {
|
||||
++ingredient_index;
|
||||
|
|
14
032array.cc
14
032array.cc
|
@ -57,10 +57,10 @@ case CREATE_ARRAY: {
|
|||
array_length_from_type = array_length_from_type->left;
|
||||
int array_length = to_integer(array_length_from_type->name);
|
||||
// initialize array length, so that size_of will work
|
||||
trace("mem") << "storing " << array_length << " in location " << base_address << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing " << array_length << " in location " << base_address << end();
|
||||
put(Memory, base_address, array_length); // in array elements
|
||||
int size = size_of(product); // in locations
|
||||
trace(9998, "run") << "creating array from " << size << " locations" << end();
|
||||
trace(Callstack_depth+1, "run") << "creating array from " << size << " locations" << end();
|
||||
// initialize array
|
||||
for (int i = 1; i <= size_of(product); ++i)
|
||||
put(Memory, base_address+i, 0);
|
||||
|
@ -274,7 +274,7 @@ case INDEX: {
|
|||
reagent/*copy*/ base = current_instruction().ingredients.at(0);
|
||||
// Update INDEX base in Run
|
||||
int base_address = base.value;
|
||||
trace(9998, "run") << "base address is " << base_address << end();
|
||||
trace(Callstack_depth+1, "run") << "base address is " << base_address << end();
|
||||
if (base_address == 0) {
|
||||
raise << maybe(current_recipe_name()) << "tried to access location 0 in '" << to_original_string(current_instruction()) << "'\n" << end();
|
||||
break;
|
||||
|
@ -288,8 +288,8 @@ case INDEX: {
|
|||
}
|
||||
reagent/*local*/ element(copy_array_element(base.type));
|
||||
element.set_value(base_address + /*skip length*/1 + index_val.at(0)*size_of(element.type));
|
||||
trace(9998, "run") << "address to copy is " << element.value << end();
|
||||
trace(9998, "run") << "its type is " << to_string(element.type) << end();
|
||||
trace(Callstack_depth+1, "run") << "address to copy is " << element.value << end();
|
||||
trace(Callstack_depth+1, "run") << "its type is " << to_string(element.type) << end();
|
||||
// Read element
|
||||
products.push_back(read_memory(element));
|
||||
break;
|
||||
|
@ -451,14 +451,14 @@ case PUT_INDEX: {
|
|||
break;
|
||||
}
|
||||
int address = base_address + /*skip length*/1 + index_val.at(0)*size_of(array_element(base.type));
|
||||
trace(9998, "run") << "address to copy to is " << address << end();
|
||||
trace(Callstack_depth+1, "run") << "address to copy to is " << address << end();
|
||||
// optimization: directly write the element rather than updating 'product'
|
||||
// and writing the entire array
|
||||
write_products = false;
|
||||
vector<double> value = read_memory(current_instruction().ingredients.at(2));
|
||||
// Write Memory in PUT_INDEX in Run
|
||||
for (int i = 0; i < SIZE(value); ++i) {
|
||||
trace("mem") << "storing " << no_scientific(value.at(i)) << " in location " << address+i << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing " << no_scientific(value.at(i)) << " in location " << address+i << end();
|
||||
put(Memory, address+i, value.at(i));
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -150,19 +150,19 @@ case MAYBE_CONVERT: {
|
|||
write_products = false;
|
||||
if (tag == static_cast<int>(get_or_insert(Memory, base_address))) {
|
||||
const reagent& variant = variant_type(base, tag);
|
||||
trace("mem") << "storing 1 in location " << status.value << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing 1 in location " << status.value << end();
|
||||
put(Memory, status.value, 1);
|
||||
if (!is_dummy(product)) {
|
||||
// Write Memory in Successful MAYBE_CONVERT in Run
|
||||
for (int i = 0; i < size_of(variant); ++i) {
|
||||
double val = get_or_insert(Memory, base_address+/*skip tag*/1+i);
|
||||
trace("mem") << "storing " << no_scientific(val) << " in location " << product.value+i << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing " << no_scientific(val) << " in location " << product.value+i << end();
|
||||
put(Memory, product.value+i, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
trace("mem") << "storing 0 in location " << status.value << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing 0 in location " << status.value << end();
|
||||
put(Memory, status.value, 0);
|
||||
}
|
||||
break;
|
||||
|
@ -305,7 +305,7 @@ $error: 0
|
|||
:(before "End check_merge_call Special-cases")
|
||||
case EXCLUSIVE_CONTAINER: {
|
||||
assert(state.data.top().container_element_index == 0);
|
||||
trace("transform") << "checking exclusive container " << to_string(container) << " vs ingredient " << ingredient_index << end();
|
||||
trace(102, "transform") << "checking exclusive container " << to_string(container) << " vs ingredient " << ingredient_index << end();
|
||||
// easy case: exact match
|
||||
if (types_strictly_match(container, inst.ingredients.at(ingredient_index)))
|
||||
return;
|
||||
|
@ -320,7 +320,7 @@ case EXCLUSIVE_CONTAINER: {
|
|||
return;
|
||||
}
|
||||
const reagent& variant = variant_type(container, ingredient.value);
|
||||
trace("transform") << "tag: " << ingredient.value << end();
|
||||
trace(102, "transform") << "tag: " << ingredient.value << end();
|
||||
// replace union with its variant
|
||||
state.data.pop();
|
||||
state.data.push(merge_check_point(variant, 0));
|
||||
|
|
|
@ -215,7 +215,7 @@ Transform.push_back(transform_new_to_allocate); // idempotent
|
|||
|
||||
:(code)
|
||||
void transform_new_to_allocate(const recipe_ordinal r) {
|
||||
trace(9991, "transform") << "--- convert 'new' to 'allocate' for recipe " << get(Recipe, r).name << end();
|
||||
trace(101, "transform") << "--- convert 'new' to 'allocate' for recipe " << get(Recipe, r).name << end();
|
||||
for (int i = 0; i < SIZE(get(Recipe, r).steps); ++i) {
|
||||
instruction& inst = get(Recipe, r).steps.at(i);
|
||||
// Convert 'new' To 'allocate'
|
||||
|
@ -224,7 +224,7 @@ void transform_new_to_allocate(const recipe_ordinal r) {
|
|||
inst.operation = ALLOCATE;
|
||||
type_tree* type = new_type_tree(inst.ingredients.at(0).name);
|
||||
inst.ingredients.at(0).set_value(size_of(type));
|
||||
trace(9992, "new") << "size of '" << inst.ingredients.at(0).name << "' is " << inst.ingredients.at(0).value << end();
|
||||
trace(102, "new") << "size of '" << inst.ingredients.at(0).name << "' is " << inst.ingredients.at(0).value << end();
|
||||
delete type;
|
||||
}
|
||||
}
|
||||
|
@ -245,7 +245,7 @@ int alloc, alloc_max;
|
|||
alloc = Memory_allocated_until;
|
||||
Memory_allocated_until += Initial_memory_per_routine;
|
||||
alloc_max = Memory_allocated_until;
|
||||
trace("new") << "routine allocated memory from " << alloc << " to " << alloc_max << end();
|
||||
trace(Callstack_depth+1, "new") << "routine allocated memory from " << alloc << " to " << alloc_max << end();
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
ALLOCATE,
|
||||
|
@ -259,16 +259,16 @@ case ALLOCATE: {
|
|||
Next_alloc_id++;
|
||||
if (SIZE(ingredients) > 1) {
|
||||
// array allocation
|
||||
trace("mem") << "array length is " << ingredients.at(1).at(0) << end();
|
||||
trace(Callstack_depth+1, "mem") << "array length is " << ingredients.at(1).at(0) << end();
|
||||
size = /*space for length*/1 + size*ingredients.at(1).at(0);
|
||||
}
|
||||
int result = allocate(size);
|
||||
// initialize alloc-id in payload
|
||||
trace("mem") << "storing alloc-id " << alloc_id << " in location " << result << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing alloc-id " << alloc_id << " in location " << result << end();
|
||||
put(Memory, result, alloc_id);
|
||||
if (SIZE(current_instruction().ingredients) > 1) {
|
||||
// initialize array length
|
||||
trace("mem") << "storing array length " << ingredients.at(1).at(0) << " in location " << result+/*skip alloc id*/1 << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing array length " << ingredients.at(1).at(0) << " in location " << result+/*skip alloc id*/1 << end();
|
||||
put(Memory, result+/*skip alloc id*/1, ingredients.at(1).at(0));
|
||||
}
|
||||
products.resize(1);
|
||||
|
@ -280,7 +280,7 @@ case ALLOCATE: {
|
|||
int allocate(int size) {
|
||||
// include space for alloc id
|
||||
++size;
|
||||
trace("mem") << "allocating size " << size << end();
|
||||
trace(Callstack_depth+1, "mem") << "allocating size " << size << end();
|
||||
//? Total_alloc += size;
|
||||
//? ++Num_alloc;
|
||||
// Allocate Special-cases
|
||||
|
@ -288,10 +288,10 @@ int allocate(int size) {
|
|||
// really crappy at the moment
|
||||
ensure_space(size);
|
||||
const int result = Current_routine->alloc;
|
||||
trace("mem") << "new alloc: " << result << end();
|
||||
trace(Callstack_depth+1, "mem") << "new alloc: " << result << end();
|
||||
// initialize allocated space
|
||||
for (int address = result; address < result+size; ++address) {
|
||||
trace("mem") << "storing 0 in location " << address << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing 0 in location " << address << end();
|
||||
put(Memory, address, 0);
|
||||
}
|
||||
Current_routine->alloc += size;
|
||||
|
@ -325,7 +325,7 @@ void ensure_space(int size) {
|
|||
Current_routine->alloc = Memory_allocated_until;
|
||||
Memory_allocated_until += Initial_memory_per_routine;
|
||||
Current_routine->alloc_max = Memory_allocated_until;
|
||||
trace("new") << "routine allocated memory from " << Current_routine->alloc << " to " << Current_routine->alloc_max << end();
|
||||
trace(Callstack_depth+1, "new") << "routine allocated memory from " << Current_routine->alloc << " to " << Current_routine->alloc_max << end();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ void lookup_memory(reagent& x) {
|
|||
void lookup_memory_core(reagent& x, bool check_for_null) {
|
||||
double address = x.value + /*skip alloc id in address*/1;
|
||||
double new_value = get_or_insert(Memory, address);
|
||||
trace("mem") << "location " << address << " contains " << no_scientific(new_value) << end();
|
||||
trace(Callstack_depth+1, "mem") << "location " << address << " contains " << no_scientific(new_value) << end();
|
||||
// check for null
|
||||
if (check_for_null && new_value == 0) {
|
||||
if (Current_routine) {
|
||||
|
|
|
@ -67,7 +67,7 @@ void abandon(int address, int payload_size) {
|
|||
for (int curr = address+1; curr < address+payload_size; ++curr)
|
||||
put(Memory, curr, 0);
|
||||
// append existing free list to address
|
||||
trace("abandon") << "saving " << address << " in free-list of size " << payload_size << end();
|
||||
trace(Callstack_depth+1, "abandon") << "saving " << address << " in free-list of size " << payload_size << end();
|
||||
put(Memory, address+/*skip invalid alloc-id*/1, get_or_insert(Current_routine->free_list, payload_size));
|
||||
put(Current_routine->free_list, payload_size, address);
|
||||
}
|
||||
|
@ -80,9 +80,9 @@ int payload_size(reagent/*copy*/ x) {
|
|||
|
||||
:(after "Allocate Special-cases")
|
||||
if (get_or_insert(Current_routine->free_list, size)) {
|
||||
trace("abandon") << "picking up space from free-list of size " << size << end();
|
||||
trace(Callstack_depth+1, "abandon") << "picking up space from free-list of size " << size << end();
|
||||
int result = get_or_insert(Current_routine->free_list, size);
|
||||
trace("mem") << "new alloc from free list: " << result << end();
|
||||
trace(Callstack_depth+1, "mem") << "new alloc from free list: " << result << end();
|
||||
put(Current_routine->free_list, size, get_or_insert(Memory, result+/*skip alloc id*/1));
|
||||
// clear 'deleted' tag
|
||||
put(Memory, result, 0);
|
||||
|
|
|
@ -31,7 +31,7 @@ if (inst.name == "new" && !inst.ingredients.empty() && is_literal_text(inst.ingr
|
|||
products.resize(1);
|
||||
products.at(0).push_back(/*alloc id*/0);
|
||||
products.at(0).push_back(new_mu_text(current_instruction().ingredients.at(0).name));
|
||||
trace("mem") << "new string alloc: " << products.at(0).at(0) << end();
|
||||
trace(Callstack_depth+1, "mem") << "new string alloc: " << products.at(0).at(0) << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ int new_mu_text(const string& contents) {
|
|||
int result = allocate(/*array length*/1 + string_length);
|
||||
int curr_address = result;
|
||||
++curr_address; // skip alloc id
|
||||
trace("mem") << "storing string length " << string_length << " in location " << curr_address << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing string length " << string_length << " in location " << curr_address << end();
|
||||
put(Memory, curr_address, string_length);
|
||||
++curr_address; // skip length
|
||||
int curr = 0;
|
||||
|
@ -53,7 +53,7 @@ int new_mu_text(const string& contents) {
|
|||
uint32_t curr_character;
|
||||
assert(curr < SIZE(contents));
|
||||
tb_utf8_char_to_unicode(&curr_character, &raw_contents[curr]);
|
||||
trace("mem") << "storing string character " << curr_character << " in location " << curr_address << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing string character " << curr_character << " in location " << curr_address << end();
|
||||
put(Memory, curr_address, curr_character);
|
||||
curr += tb_utf8_char_length(raw_contents[curr]);
|
||||
++curr_address;
|
||||
|
|
16
040brace.cc
16
040brace.cc
|
@ -39,15 +39,15 @@ void transform_braces(const recipe_ordinal r) {
|
|||
const bool OPEN = false, CLOSE = true;
|
||||
// use signed integer for step index because we'll be doing arithmetic on it
|
||||
list<pair<bool/*OPEN/CLOSE*/, /*step*/int> > braces;
|
||||
trace(9991, "transform") << "--- transform braces for recipe " << get(Recipe, r).name << end();
|
||||
trace(101, "transform") << "--- transform braces for recipe " << get(Recipe, r).name << end();
|
||||
for (int index = 0; index < SIZE(get(Recipe, r).steps); ++index) {
|
||||
const instruction& inst = get(Recipe, r).steps.at(index);
|
||||
if (inst.label == "{") {
|
||||
trace(9993, "transform") << maybe(get(Recipe, r).name) << "push (open, " << index << ")" << end();
|
||||
trace(103, "transform") << maybe(get(Recipe, r).name) << "push (open, " << index << ")" << end();
|
||||
braces.push_back(pair<bool,int>(OPEN, index));
|
||||
}
|
||||
if (inst.label == "}") {
|
||||
trace(9993, "transform") << "push (close, " << index << ")" << end();
|
||||
trace(103, "transform") << "push (close, " << index << ")" << end();
|
||||
braces.push_back(pair<bool,int>(CLOSE, index));
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ void transform_braces(const recipe_ordinal r) {
|
|||
&& inst.name != "break"
|
||||
&& inst.name != "break-if"
|
||||
&& inst.name != "break-unless") {
|
||||
trace(9992, "transform") << inst.name << " ..." << end();
|
||||
trace(102, "transform") << inst.name << " ..." << end();
|
||||
continue;
|
||||
}
|
||||
// check for errors
|
||||
|
@ -101,14 +101,14 @@ void transform_braces(const recipe_ordinal r) {
|
|||
if (inst.name.find("-if") != string::npos || inst.name.find("-unless") != string::npos) {
|
||||
// conditional branches check arg 1
|
||||
if (SIZE(inst.ingredients) > 1 && is_literal(inst.ingredients.at(1))) {
|
||||
trace(9992, "transform") << inst.name << ' ' << inst.ingredients.at(1).name << ":offset" << end();
|
||||
trace(102, "transform") << inst.name << ' ' << inst.ingredients.at(1).name << ":offset" << end();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// unconditional branches check arg 0
|
||||
if (!inst.ingredients.empty() && is_literal(inst.ingredients.at(0))) {
|
||||
trace(9992, "transform") << "jump " << inst.ingredients.at(0).name << ":offset" << end();
|
||||
trace(102, "transform") << "jump " << inst.ingredients.at(0).name << ":offset" << end();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -124,9 +124,9 @@ void transform_braces(const recipe_ordinal r) {
|
|||
inst.ingredients.push_back(target);
|
||||
// log computed target
|
||||
if (inst.name == "jump")
|
||||
trace(9992, "transform") << "jump " << no_scientific(target.value) << ":offset" << end();
|
||||
trace(102, "transform") << "jump " << no_scientific(target.value) << ":offset" << end();
|
||||
else
|
||||
trace(9992, "transform") << inst.name << ' ' << inst.ingredients.at(0).name << ", " << no_scientific(target.value) << ":offset" << end();
|
||||
trace(102, "transform") << inst.name << ' ' << inst.ingredients.at(0).name << ", " << no_scientific(target.value) << ":offset" << end();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ Name = Name_snapshot;
|
|||
:(code)
|
||||
void transform_names(const recipe_ordinal r) {
|
||||
recipe& caller = get(Recipe, r);
|
||||
trace(9991, "transform") << "--- transform names for recipe " << caller.name << end();
|
||||
trace(101, "transform") << "--- transform names for recipe " << caller.name << end();
|
||||
bool names_used = false;
|
||||
bool numeric_locations_used = false;
|
||||
map<string, int>& names = Name[r];
|
||||
|
@ -78,7 +78,7 @@ void transform_names(const recipe_ordinal r) {
|
|||
if (is_named_location(product)) names_used = true;
|
||||
if (is_integer(product.name)) continue;
|
||||
if (names.find(product.name) == names.end()) {
|
||||
trace(9993, "name") << "assign " << product.name << " " << curr_idx << end();
|
||||
trace(103, "name") << "assign " << product.name << " " << curr_idx << end();
|
||||
names[product.name] = curr_idx;
|
||||
curr_idx += size_of(product);
|
||||
}
|
||||
|
@ -282,7 +282,7 @@ if (inst.name == "get" || inst.name == "get-location" || inst.name == "put") {
|
|||
type_ordinal base_type = skip_addresses(inst.ingredients.at(0).type);
|
||||
if (contains_key(Type, base_type)) { // otherwise we'll raise an error elsewhere
|
||||
inst.ingredients.at(1).set_value(find_element_offset(base_type, inst.ingredients.at(1).name, get(Recipe, r).name));
|
||||
trace(9993, "name") << "element " << inst.ingredients.at(1).name << " of type " << get(Type, base_type).name << " is at offset " << no_scientific(inst.ingredients.at(1).value) << end();
|
||||
trace(103, "name") << "element " << inst.ingredients.at(1).name << " of type " << get(Type, base_type).name << " is at offset " << no_scientific(inst.ingredients.at(1).value) << end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -330,7 +330,7 @@ if (inst.name == "maybe-convert") {
|
|||
type_ordinal base_type = skip_addresses(inst.ingredients.at(0).type);
|
||||
if (contains_key(Type, base_type)) { // otherwise we'll raise an error elsewhere
|
||||
inst.ingredients.at(1).set_value(find_element_offset(base_type, inst.ingredients.at(1).name, get(Recipe, r).name));
|
||||
trace(9993, "name") << "variant " << inst.ingredients.at(1).name << " of type " << get(Type, base_type).name << " has tag " << no_scientific(inst.ingredients.at(1).value) << end();
|
||||
trace(103, "name") << "variant " << inst.ingredients.at(1).name << " of type " << get(Type, base_type).name << " has tag " << no_scientific(inst.ingredients.at(1).value) << end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -264,7 +264,7 @@ void check_default_space(const recipe_ordinal r) {
|
|||
// End check_default_space Special-cases
|
||||
// assume recipes with only numeric addresses know what they're doing (usually tests)
|
||||
if (!contains_non_special_name(r)) return;
|
||||
trace(9991, "transform") << "--- check that recipe " << caller.name << " sets default-space" << end();
|
||||
trace(101, "transform") << "--- check that recipe " << caller.name << " sets default-space" << end();
|
||||
if (caller.steps.empty()) return;
|
||||
if (!starts_by_setting_default_space(caller))
|
||||
raise << caller.name << " does not seem to start with 'local-scope' or 'default-space'\n" << end();
|
||||
|
|
|
@ -42,7 +42,7 @@ Transform.push_back(collect_surrounding_spaces); // idempotent
|
|||
|
||||
:(code)
|
||||
void collect_surrounding_spaces(const recipe_ordinal r) {
|
||||
trace(9991, "transform") << "--- collect surrounding spaces for recipe " << get(Recipe, r).name << end();
|
||||
trace(101, "transform") << "--- collect surrounding spaces for recipe " << get(Recipe, r).name << end();
|
||||
for (int i = 0; i < SIZE(get(Recipe, r).steps); ++i) {
|
||||
const instruction& inst = get(Recipe, r).steps.at(i);
|
||||
if (inst.is_label) continue;
|
||||
|
@ -69,7 +69,7 @@ void collect_surrounding_spaces(const recipe_ordinal r) {
|
|||
raise << "recipe '" << get(Recipe, r).name << "' can have only one 'surrounding' recipe but has '" << get(Recipe, get(Surrounding_space, r)).name << "' and '" << surrounding_recipe_name << "'\n" << end();
|
||||
continue;
|
||||
}
|
||||
trace(9993, "name") << "lexically surrounding space for recipe " << get(Recipe, r).name << " comes from " << surrounding_recipe_name << end();
|
||||
trace(103, "name") << "lexically surrounding space for recipe " << get(Recipe, r).name << " comes from " << surrounding_recipe_name << end();
|
||||
if (!contains_key(Recipe_ordinal, surrounding_recipe_name)) {
|
||||
raise << "can't find recipe providing surrounding space for '" << get(Recipe, r).name << "'; looking for '" << surrounding_recipe_name << "'\n" << end();
|
||||
continue;
|
||||
|
|
|
@ -34,7 +34,7 @@ struct name_lt {
|
|||
:(code)
|
||||
void check_or_set_types_by_name(const recipe_ordinal r) {
|
||||
recipe& caller = get(Recipe, r);
|
||||
trace(9991, "transform") << "--- deduce types for recipe " << caller.name << end();
|
||||
trace(101, "transform") << "--- deduce types for recipe " << caller.name << end();
|
||||
for (int i = 0; i < SIZE(caller.steps); ++i) {
|
||||
instruction& inst = caller.steps.at(i);
|
||||
for (int in = 0; in < SIZE(inst.ingredients); ++in)
|
||||
|
@ -62,7 +62,7 @@ void deduce_missing_type(set<reagent, name_lt>& known_types, reagent& x, const r
|
|||
if (known_types.find(x) == known_types.end()) return;
|
||||
const reagent& exemplar = *known_types.find(x);
|
||||
x.type = new type_tree(*exemplar.type);
|
||||
trace(9992, "transform") << x.name << " <= " << names_to_string(x.type) << end();
|
||||
trace(102, "transform") << x.name << " <= " << names_to_string(x.type) << end();
|
||||
// spaces are special; their type includes their /names property
|
||||
if (is_mu_space(x) && !has_property(x, "names")) {
|
||||
if (!has_property(exemplar, "names")) {
|
||||
|
@ -82,7 +82,7 @@ void check_type(set<reagent, name_lt>& known_types, const reagent& x, const reci
|
|||
return;
|
||||
}
|
||||
if (known_types.find(x) == known_types.end()) {
|
||||
trace(9992, "transform") << x.name << " => " << names_to_string(x.type) << end();
|
||||
trace(102, "transform") << x.name << " => " << names_to_string(x.type) << end();
|
||||
known_types.insert(x);
|
||||
}
|
||||
if (!types_strictly_match(known_types.find(x)->type, x.type)) {
|
||||
|
|
|
@ -225,7 +225,6 @@ void run_mu_scenario(const scenario& s) {
|
|||
if (!Hide_errors && trace_contains_errors() && !Scenario_testing_scenario)
|
||||
Passed = false;
|
||||
if (not_already_inside_test && Trace_stream) {
|
||||
if (Save_trace) Trace_stream->save();
|
||||
delete Trace_stream;
|
||||
Trace_stream = NULL;
|
||||
}
|
||||
|
@ -423,7 +422,7 @@ void check_memory(const string& s) {
|
|||
double value = to_double(rhs);
|
||||
if (contains_key(locations_checked, address))
|
||||
raise << maybe(current_recipe_name()) << "duplicate expectation for location '" << address << "'\n" << end();
|
||||
trace("run") << "checking location " << address << end();
|
||||
trace(Callstack_depth+1, "run") << "checking location " << address << end();
|
||||
if (get_or_insert(Memory, address) != value) {
|
||||
if (!Hide_errors) cerr << '\n';
|
||||
raise << "F - " << maybe(current_recipe_name()) << "expected location '" << address << "' to contain " << no_scientific(value) << " but saw " << no_scientific(get_or_insert(Memory, address)) << '\n' << end();
|
||||
|
@ -469,7 +468,7 @@ void check_type(const string& lhs, istream& in) {
|
|||
}
|
||||
|
||||
void check_mu_text(int start, const string& literal) {
|
||||
trace("run") << "checking text length at " << start << end();
|
||||
trace(Callstack_depth+1, "run") << "checking text length at " << start << end();
|
||||
int array_length = static_cast<int>(get_or_insert(Memory, start));
|
||||
if (array_length != SIZE(literal)) {
|
||||
if (!Hide_errors) cerr << '\n';
|
||||
|
@ -479,7 +478,7 @@ void check_mu_text(int start, const string& literal) {
|
|||
}
|
||||
int curr = start+1; // now skip length
|
||||
for (int i = 0; i < SIZE(literal); ++i) {
|
||||
trace("run") << "checking location " << curr+i << end();
|
||||
trace(Callstack_depth+1, "run") << "checking location " << curr+i << end();
|
||||
if (get_or_insert(Memory, curr+i) != literal.at(i)) {
|
||||
if (!Hide_errors) cerr << '\n';
|
||||
raise << "F - " << maybe(current_recipe_name()) << "expected location " << (curr+i) << " to contain " << literal.at(i) << " but saw " << no_scientific(get_or_insert(Memory, curr+i)) << '\n' << end();
|
||||
|
@ -620,16 +619,18 @@ void check_trace(const string& expected) {
|
|||
vector<trace_line> parse_trace(const string& expected) {
|
||||
vector<string> buf = split(expected, "\n");
|
||||
vector<trace_line> result;
|
||||
const string separator = ": ";
|
||||
for (int i = 0; i < SIZE(buf); ++i) {
|
||||
buf.at(i) = trim(buf.at(i));
|
||||
if (buf.at(i).empty()) continue;
|
||||
int delim = buf.at(i).find(": ");
|
||||
int delim = buf.at(i).find(separator);
|
||||
if (delim == -1) {
|
||||
raise << maybe(current_recipe_name()) << "lines in 'trace-should-contain' should be of the form <label>: <contents>. Both parts are required.\n" << end();
|
||||
result.clear();
|
||||
return result;
|
||||
}
|
||||
result.push_back(trace_line(trim(buf.at(i).substr(0, delim)), trim(buf.at(i).substr(delim+2))));
|
||||
result.push_back(trace_line(/*contents*/ trim(buf.at(i).substr(delim+SIZE(separator) )),
|
||||
/*label*/ trim(buf.at(i).substr(0, delim))));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ void insert_fragments(const recipe_ordinal r) {
|
|||
}
|
||||
|
||||
void insert_fragments(recipe& r) {
|
||||
trace(9991, "transform") << "--- insert fragments into recipe " << r.name << end();
|
||||
trace(101, "transform") << "--- insert fragments into recipe " << r.name << end();
|
||||
bool made_progress = true;
|
||||
int pass = 0;
|
||||
while (made_progress) {
|
||||
|
@ -111,12 +111,12 @@ void insert_fragments(recipe& r) {
|
|||
// ok to use contains_key even though Before_fragments uses [],
|
||||
// because appending an empty recipe is a noop
|
||||
if (contains_key(Before_fragments, inst.label)) {
|
||||
trace(9992, "transform") << "insert fragments before label " << inst.label << end();
|
||||
trace(102, "transform") << "insert fragments before label " << inst.label << end();
|
||||
append_fragment(result, Before_fragments[inst.label].steps, prefix.str());
|
||||
}
|
||||
result.push_back(inst);
|
||||
if (contains_key(After_fragments, inst.label)) {
|
||||
trace(9992, "transform") << "insert fragments after label " << inst.label << end();
|
||||
trace(102, "transform") << "insert fragments after label " << inst.label << end();
|
||||
append_fragment(result, After_fragments[inst.label].steps, prefix.str());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ has_header = false;
|
|||
|
||||
:(before "End Recipe Refinements")
|
||||
if (in.peek() != '[') {
|
||||
trace("parse") << "recipe has a header; parsing" << end();
|
||||
trace(101, "parse") << "recipe has a header; parsing" << end();
|
||||
load_recipe_header(in, result);
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ void load_recipe_header(istream& in, recipe& result) {
|
|||
raise << "recipe " << result.name << " should say '->' and not '<-'\n" << end();
|
||||
if (s == "->") break;
|
||||
result.ingredients.push_back(reagent(s));
|
||||
trace("parse") << "header ingredient: " << result.ingredients.back().original_string << end();
|
||||
trace(101, "parse") << "header ingredient: " << result.ingredients.back().original_string << end();
|
||||
skip_whitespace_but_not_newline(in);
|
||||
}
|
||||
while (has_data(in) && in.peek() != '[' && in.peek() != '\n') {
|
||||
|
@ -53,7 +53,7 @@ void load_recipe_header(istream& in, recipe& result) {
|
|||
return;
|
||||
}
|
||||
result.products.push_back(reagent(s));
|
||||
trace("parse") << "header product: " << result.products.back().original_string << end();
|
||||
trace(101, "parse") << "header product: " << result.products.back().original_string << end();
|
||||
skip_whitespace_but_not_newline(in);
|
||||
}
|
||||
// End Load Recipe Header(result)
|
||||
|
@ -149,7 +149,7 @@ if (!result.has_header) {
|
|||
}
|
||||
}
|
||||
if (result.has_header) {
|
||||
trace("parse") << "recipe " << result.name << " has a header" << end();
|
||||
trace(101, "parse") << "recipe " << result.name << " has a header" << end();
|
||||
}
|
||||
|
||||
//: Support type abbreviations in headers.
|
||||
|
@ -288,7 +288,7 @@ Transform.push_back(check_calls_against_header); // idempotent
|
|||
:(code)
|
||||
void check_calls_against_header(const recipe_ordinal r) {
|
||||
const recipe& caller = get(Recipe, r);
|
||||
trace(9991, "transform") << "--- type-check calls inside recipe " << caller.name << end();
|
||||
trace(101, "transform") << "--- type-check calls inside recipe " << caller.name << end();
|
||||
for (int i = 0; i < SIZE(caller.steps); ++i) {
|
||||
const instruction& inst = caller.steps.at(i);
|
||||
if (is_primitive(inst.operation)) continue;
|
||||
|
@ -332,7 +332,7 @@ Transform.push_back(check_return_instructions_against_header); // idempotent
|
|||
void check_return_instructions_against_header(const recipe_ordinal r) {
|
||||
const recipe& caller_recipe = get(Recipe, r);
|
||||
if (!caller_recipe.has_header) return;
|
||||
trace(9991, "transform") << "--- checking return instructions against header for " << caller_recipe.name << end();
|
||||
trace(101, "transform") << "--- checking return instructions against header for " << caller_recipe.name << end();
|
||||
for (int i = 0; i < SIZE(caller_recipe.steps); ++i) {
|
||||
const instruction& inst = caller_recipe.steps.at(i);
|
||||
if (inst.name != "reply" && inst.name != "return") continue;
|
||||
|
@ -403,7 +403,7 @@ Transform.push_back(check_header_ingredients); // idempotent
|
|||
void check_header_ingredients(const recipe_ordinal r) {
|
||||
recipe& caller_recipe = get(Recipe, r);
|
||||
caller_recipe.ingredient_index.clear();
|
||||
trace(9991, "transform") << "--- checking return instructions against header for " << caller_recipe.name << end();
|
||||
trace(101, "transform") << "--- checking return instructions against header for " << caller_recipe.name << end();
|
||||
for (int i = 0; i < SIZE(caller_recipe.ingredients); ++i) {
|
||||
if (caller_recipe.ingredients.at(i).type == NULL)
|
||||
raise << maybe(caller_recipe.name) << "ingredient '" << caller_recipe.ingredients.at(i).name << "' has no type\n" << end();
|
||||
|
@ -435,37 +435,37 @@ Transform.push_back(deduce_types_from_header); // idempotent
|
|||
void deduce_types_from_header(const recipe_ordinal r) {
|
||||
recipe& caller_recipe = get(Recipe, r);
|
||||
if (caller_recipe.products.empty()) return;
|
||||
trace(9991, "transform") << "--- deduce types from header for " << caller_recipe.name << end();
|
||||
trace(101, "transform") << "--- deduce types from header for " << caller_recipe.name << end();
|
||||
map<string, const type_tree*> header_type;
|
||||
for (int i = 0; i < SIZE(caller_recipe.ingredients); ++i) {
|
||||
if (!caller_recipe.ingredients.at(i).type) continue; // error handled elsewhere
|
||||
put(header_type, caller_recipe.ingredients.at(i).name, caller_recipe.ingredients.at(i).type);
|
||||
trace(9993, "transform") << "type of " << caller_recipe.ingredients.at(i).name << " is " << names_to_string(caller_recipe.ingredients.at(i).type) << end();
|
||||
trace(103, "transform") << "type of " << caller_recipe.ingredients.at(i).name << " is " << names_to_string(caller_recipe.ingredients.at(i).type) << end();
|
||||
}
|
||||
for (int i = 0; i < SIZE(caller_recipe.products); ++i) {
|
||||
if (!caller_recipe.products.at(i).type) continue; // error handled elsewhere
|
||||
put(header_type, caller_recipe.products.at(i).name, caller_recipe.products.at(i).type);
|
||||
trace(9993, "transform") << "type of " << caller_recipe.products.at(i).name << " is " << names_to_string(caller_recipe.products.at(i).type) << end();
|
||||
trace(103, "transform") << "type of " << caller_recipe.products.at(i).name << " is " << names_to_string(caller_recipe.products.at(i).type) << end();
|
||||
}
|
||||
for (int i = 0; i < SIZE(caller_recipe.steps); ++i) {
|
||||
instruction& inst = caller_recipe.steps.at(i);
|
||||
trace(9992, "transform") << "instruction: " << to_string(inst) << end();
|
||||
trace(102, "transform") << "instruction: " << to_string(inst) << end();
|
||||
for (int i = 0; i < SIZE(inst.ingredients); ++i) {
|
||||
if (inst.ingredients.at(i).type) continue;
|
||||
if (header_type.find(inst.ingredients.at(i).name) == header_type.end())
|
||||
continue;
|
||||
if (!contains_key(header_type, inst.ingredients.at(i).name)) continue; // error handled elsewhere
|
||||
inst.ingredients.at(i).type = new type_tree(*get(header_type, inst.ingredients.at(i).name));
|
||||
trace(9993, "transform") << "type of " << inst.ingredients.at(i).name << " is " << names_to_string(inst.ingredients.at(i).type) << end();
|
||||
trace(103, "transform") << "type of " << inst.ingredients.at(i).name << " is " << names_to_string(inst.ingredients.at(i).type) << end();
|
||||
}
|
||||
for (int i = 0; i < SIZE(inst.products); ++i) {
|
||||
trace(9993, "transform") << " product: " << to_string(inst.products.at(i)) << end();
|
||||
trace(103, "transform") << " product: " << to_string(inst.products.at(i)) << end();
|
||||
if (inst.products.at(i).type) continue;
|
||||
if (header_type.find(inst.products.at(i).name) == header_type.end())
|
||||
continue;
|
||||
if (!contains_key(header_type, inst.products.at(i).name)) continue; // error handled elsewhere
|
||||
inst.products.at(i).type = new type_tree(*get(header_type, inst.products.at(i).name));
|
||||
trace(9993, "transform") << "type of " << inst.products.at(i).name << " is " << names_to_string(inst.products.at(i).type) << end();
|
||||
trace(103, "transform") << "type of " << inst.products.at(i).name << " is " << names_to_string(inst.products.at(i).type) << end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -491,7 +491,7 @@ Transform.push_back(fill_in_return_ingredients); // idempotent
|
|||
:(code)
|
||||
void fill_in_return_ingredients(const recipe_ordinal r) {
|
||||
recipe& caller_recipe = get(Recipe, r);
|
||||
trace(9991, "transform") << "--- fill in return ingredients from header for recipe " << caller_recipe.name << end();
|
||||
trace(101, "transform") << "--- fill in return ingredients from header for recipe " << caller_recipe.name << end();
|
||||
if (!caller_recipe.has_header) return;
|
||||
for (int i = 0; i < SIZE(caller_recipe.steps); ++i) {
|
||||
instruction& inst = caller_recipe.steps.at(i);
|
||||
|
@ -624,7 +624,7 @@ Transform.push_back(check_recipe_header_constraints);
|
|||
void check_recipe_header_constraints(const recipe_ordinal r) {
|
||||
const recipe& caller = get(Recipe, r);
|
||||
if (caller.name != "main") return;
|
||||
trace(9992, "transform") << "check recipe header constraints for recipe " << caller.name << end();
|
||||
trace(102, "transform") << "check recipe header constraints for recipe " << caller.name << end();
|
||||
if (!caller.has_header) return;
|
||||
reagent/*local*/ expected_ingredient("x:address:array:character");
|
||||
for (int i = 0; i < SIZE(caller.ingredients); ++i) {
|
||||
|
|
|
@ -41,7 +41,7 @@ if (result.name != "main" && contains_key(Recipe_ordinal, result.name)) {
|
|||
put(Recipe_ordinal, new_name, Next_recipe_ordinal++);
|
||||
get_or_insert(Recipe_variants, result.name).push_back(get(Recipe_ordinal, new_name));
|
||||
}
|
||||
trace("load") << "switching " << result.name << " to " << new_name << end();
|
||||
trace(101, "load") << "switching " << result.name << " to " << new_name << end();
|
||||
result.name = new_name;
|
||||
result.is_autogenerated = true;
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ list<call> Resolve_stack;
|
|||
:(code)
|
||||
void resolve_ambiguous_calls(const recipe_ordinal r) {
|
||||
recipe& caller_recipe = get(Recipe, r);
|
||||
trace(9991, "transform") << "--- resolve ambiguous calls for recipe " << caller_recipe.name << end();
|
||||
trace(101, "transform") << "--- resolve ambiguous calls for recipe " << caller_recipe.name << end();
|
||||
for (int index = 0; index < SIZE(caller_recipe.steps); ++index) {
|
||||
instruction& inst = caller_recipe.steps.at(index);
|
||||
if (inst.is_label) continue;
|
||||
|
@ -173,7 +173,7 @@ void resolve_ambiguous_calls(const recipe_ordinal r) {
|
|||
void resolve_ambiguous_call(const recipe_ordinal r, int index, instruction& inst, const recipe& caller_recipe) {
|
||||
// End resolve_ambiguous_call(r, index, inst, caller_recipe) Special-cases
|
||||
if (non_ghost_size(get_or_insert(Recipe_variants, inst.name)) == 0) return;
|
||||
trace(9992, "transform") << "instruction " << to_original_string(inst) << end();
|
||||
trace(102, "transform") << "instruction " << to_original_string(inst) << end();
|
||||
Resolve_stack.push_front(call(r, index));
|
||||
string new_name = best_variant(inst, caller_recipe);
|
||||
if (!new_name.empty())
|
||||
|
@ -241,7 +241,7 @@ vector<recipe_ordinal> strictly_matching_variants(const instruction& inst, const
|
|||
vector<recipe_ordinal> result;
|
||||
for (int i = 0; i < SIZE(variants); ++i) {
|
||||
if (variants.at(i) == -1) continue;
|
||||
trace(9992, "transform") << "checking variant (strict) " << i << ": " << header_label(variants.at(i)) << end();
|
||||
trace(102, "transform") << "checking variant (strict) " << i << ": " << header_label(variants.at(i)) << end();
|
||||
if (all_header_reagents_strictly_match(inst, get(Recipe, variants.at(i))))
|
||||
result.push_back(variants.at(i));
|
||||
}
|
||||
|
@ -251,14 +251,14 @@ vector<recipe_ordinal> strictly_matching_variants(const instruction& inst, const
|
|||
bool all_header_reagents_strictly_match(const instruction& inst, const recipe& variant) {
|
||||
for (int i = 0; i < min(SIZE(inst.ingredients), SIZE(variant.ingredients)); ++i) {
|
||||
if (!types_strictly_match(variant.ingredients.at(i), inst.ingredients.at(i))) {
|
||||
trace(9993, "transform") << "strict match failed: ingredient " << i << end();
|
||||
trace(103, "transform") << "strict match failed: ingredient " << i << end();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < min(SIZE(inst.products), SIZE(variant.products)); ++i) {
|
||||
if (is_dummy(inst.products.at(i))) continue;
|
||||
if (!types_strictly_match(variant.products.at(i), inst.products.at(i))) {
|
||||
trace(9993, "transform") << "strict match failed: product " << i << end();
|
||||
trace(103, "transform") << "strict match failed: product " << i << end();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -270,7 +270,7 @@ vector<recipe_ordinal> matching_variants(const instruction& inst, const vector<r
|
|||
vector<recipe_ordinal> result;
|
||||
for (int i = 0; i < SIZE(variants); ++i) {
|
||||
if (variants.at(i) == -1) continue;
|
||||
trace(9992, "transform") << "checking variant " << i << ": " << header_label(variants.at(i)) << end();
|
||||
trace(102, "transform") << "checking variant " << i << ": " << header_label(variants.at(i)) << end();
|
||||
if (all_header_reagents_match(inst, get(Recipe, variants.at(i))))
|
||||
result.push_back(variants.at(i));
|
||||
}
|
||||
|
@ -280,14 +280,14 @@ vector<recipe_ordinal> matching_variants(const instruction& inst, const vector<r
|
|||
bool all_header_reagents_match(const instruction& inst, const recipe& variant) {
|
||||
for (int i = 0; i < min(SIZE(inst.ingredients), SIZE(variant.ingredients)); ++i) {
|
||||
if (!types_match(variant.ingredients.at(i), inst.ingredients.at(i))) {
|
||||
trace(9993, "transform") << "match failed: ingredient " << i << end();
|
||||
trace(103, "transform") << "match failed: ingredient " << i << end();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < min(SIZE(variant.products), SIZE(inst.products)); ++i) {
|
||||
if (is_dummy(inst.products.at(i))) continue;
|
||||
if (!types_match(variant.products.at(i), inst.products.at(i))) {
|
||||
trace(9993, "transform") << "match failed: product " << i << end();
|
||||
trace(103, "transform") << "match failed: product " << i << end();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,7 +132,7 @@ if (!info.type_ingredient_names.empty()) continue;
|
|||
|
||||
:(before "End container Name Refinements")
|
||||
if (name.find(':') != string::npos) {
|
||||
trace("parse") << "container has type ingredients; parsing" << end();
|
||||
trace(101, "parse") << "container has type ingredients; parsing" << end();
|
||||
if (!read_type_ingredients(name, command)) {
|
||||
// error; skip rest of the container definition and continue
|
||||
slurp_balanced_bracket(in);
|
||||
|
|
|
@ -50,10 +50,10 @@ if (contains_type_ingredient_name(to)) return false;
|
|||
candidates = strictly_matching_shape_shifting_variants(inst, variants);
|
||||
if (!candidates.empty()) {
|
||||
recipe_ordinal exemplar = best_shape_shifting_variant(inst, candidates);
|
||||
trace(9992, "transform") << "found variant to specialize: " << exemplar << ' ' << get(Recipe, exemplar).name << end();
|
||||
trace(102, "transform") << "found variant to specialize: " << exemplar << ' ' << get(Recipe, exemplar).name << end();
|
||||
string new_recipe_name = insert_new_variant(exemplar, inst, caller_recipe);
|
||||
if (new_recipe_name != "") {
|
||||
trace(9992, "transform") << "new specialization: " << new_recipe_name << end();
|
||||
trace(102, "transform") << "new specialization: " << new_recipe_name << end();
|
||||
return new_recipe_name;
|
||||
}
|
||||
}
|
||||
|
@ -84,14 +84,14 @@ vector<recipe_ordinal> strictly_matching_shape_shifting_variants(const instructi
|
|||
bool all_concrete_header_reagents_strictly_match(const instruction& inst, const recipe& variant) {
|
||||
for (int i = 0; i < min(SIZE(inst.ingredients), SIZE(variant.ingredients)); ++i) {
|
||||
if (!concrete_type_names_strictly_match(variant.ingredients.at(i), inst.ingredients.at(i))) {
|
||||
trace(9993, "transform") << "concrete-type match failed: ingredient " << i << end();
|
||||
trace(103, "transform") << "concrete-type match failed: ingredient " << i << end();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < min(SIZE(inst.products), SIZE(variant.products)); ++i) {
|
||||
if (is_dummy(inst.products.at(i))) continue;
|
||||
if (!concrete_type_names_strictly_match(variant.products.at(i), inst.products.at(i))) {
|
||||
trace(9993, "transform") << "concrete-type match failed: product " << i << end();
|
||||
trace(103, "transform") << "concrete-type match failed: product " << i << end();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -250,10 +250,10 @@ string insert_new_variant(recipe_ordinal exemplar, const instruction& inst, cons
|
|||
new_recipe.name = new_name;
|
||||
new_recipe.ordinal = new_recipe_ordinal;
|
||||
new_recipe.is_autogenerated = true;
|
||||
trace(9993, "transform") << "switching " << inst.name << " to specialized " << header_label(new_recipe) << end();
|
||||
trace(103, "transform") << "switching " << inst.name << " to specialized " << header_label(new_recipe) << end();
|
||||
|
||||
trace(9992, "transform") << "transforming new specialization: " << new_recipe.name << end();
|
||||
trace(9992, "transform") << new_recipe.name << ": performing transforms until check_or_set_types_by_name" << end();
|
||||
trace(102, "transform") << "transforming new specialization: " << new_recipe.name << end();
|
||||
trace(102, "transform") << new_recipe.name << ": performing transforms until check_or_set_types_by_name" << end();
|
||||
int transform_index = 0;
|
||||
for (transform_index = 0; transform_index < SIZE(Transform); ++transform_index) {
|
||||
if (Transform.at(transform_index) == check_or_set_types_by_name) break;
|
||||
|
@ -261,11 +261,11 @@ string insert_new_variant(recipe_ordinal exemplar, const instruction& inst, cons
|
|||
}
|
||||
new_recipe.transformed_until = transform_index-1;
|
||||
|
||||
trace(9992, "transform") << new_recipe.name << ": performing type-ingredient-aware version of transform check_or_set_types_by_name" << end();
|
||||
trace(102, "transform") << new_recipe.name << ": performing type-ingredient-aware version of transform check_or_set_types_by_name" << end();
|
||||
compute_type_names(new_recipe);
|
||||
new_recipe.transformed_until++;
|
||||
|
||||
trace(9992, "transform") << new_recipe.name << ": replacing type ingredients" << end();
|
||||
trace(102, "transform") << new_recipe.name << ": replacing type ingredients" << end();
|
||||
{
|
||||
map<string, const type_tree*> mappings;
|
||||
bool error = false;
|
||||
|
@ -278,9 +278,9 @@ string insert_new_variant(recipe_ordinal exemplar, const instruction& inst, cons
|
|||
}
|
||||
ensure_all_concrete_types(new_recipe, get(Recipe, exemplar));
|
||||
|
||||
trace(9992, "transform") << new_recipe.name << ": recording the new variant before recursively calling resolve_ambiguous_calls" << end();
|
||||
trace(102, "transform") << new_recipe.name << ": recording the new variant before recursively calling resolve_ambiguous_calls" << end();
|
||||
get(Recipe_variants, inst.name).push_back(new_recipe_ordinal);
|
||||
trace(9992, "transform") << new_recipe.name << ": performing remaining transforms (including resolve_ambiguous_calls)" << end();
|
||||
trace(102, "transform") << new_recipe.name << ": performing remaining transforms (including resolve_ambiguous_calls)" << end();
|
||||
for (/*nada*/; transform_index < SIZE(Transform); ++transform_index)
|
||||
(*Transform.at(transform_index))(new_recipe_ordinal);
|
||||
new_recipe.transformed_until = SIZE(Transform)-1;
|
||||
|
@ -288,7 +288,7 @@ string insert_new_variant(recipe_ordinal exemplar, const instruction& inst, cons
|
|||
}
|
||||
|
||||
void compute_type_names(recipe& variant) {
|
||||
trace(9993, "transform") << "-- compute type names: " << variant.name << end();
|
||||
trace(103, "transform") << "-- compute type names: " << variant.name << end();
|
||||
map<string, type_tree*> type_names;
|
||||
for (int i = 0; i < SIZE(variant.ingredients); ++i)
|
||||
save_or_deduce_type_name(variant.ingredients.at(i), type_names, variant, "");
|
||||
|
@ -296,7 +296,7 @@ void compute_type_names(recipe& variant) {
|
|||
save_or_deduce_type_name(variant.products.at(i), type_names, variant, "");
|
||||
for (int i = 0; i < SIZE(variant.steps); ++i) {
|
||||
instruction& inst = variant.steps.at(i);
|
||||
trace(9993, "transform") << " instruction: " << to_string(inst) << end();
|
||||
trace(103, "transform") << " instruction: " << to_string(inst) << end();
|
||||
for (int in = 0; in < SIZE(inst.ingredients); ++in)
|
||||
save_or_deduce_type_name(inst.ingredients.at(in), type_names, variant, " in '" + to_original_string(inst) + "'");
|
||||
for (int out = 0; out < SIZE(inst.products); ++out)
|
||||
|
@ -305,10 +305,10 @@ void compute_type_names(recipe& variant) {
|
|||
}
|
||||
|
||||
void save_or_deduce_type_name(reagent& x, map<string, type_tree*>& type, const recipe& variant, const string& context) {
|
||||
trace(9994, "transform") << " checking " << to_string(x) << ": " << names_to_string(x.type) << end();
|
||||
trace(104, "transform") << " checking " << to_string(x) << ": " << names_to_string(x.type) << end();
|
||||
if (!x.type && contains_key(type, x.name)) {
|
||||
x.type = new type_tree(*get(type, x.name));
|
||||
trace(9994, "transform") << " deducing type to " << names_to_string(x.type) << end();
|
||||
trace(104, "transform") << " deducing type to " << names_to_string(x.type) << end();
|
||||
return;
|
||||
}
|
||||
// Type Check in Type-ingredient-aware check_or_set_types_by_name
|
||||
|
@ -322,7 +322,7 @@ void save_or_deduce_type_name(reagent& x, map<string, type_tree*>& type, const r
|
|||
if (contains_key(type, x.name)) return;
|
||||
if (x.type->name == "offset" || x.type->name == "variant") return; // special-case for container-access instructions
|
||||
put(type, x.name, x.type);
|
||||
trace(9993, "transform") << "type of '" << x.name << "' is " << names_to_string(x.type) << end();
|
||||
trace(103, "transform") << "type of '" << x.name << "' is " << names_to_string(x.type) << end();
|
||||
}
|
||||
|
||||
void compute_type_ingredient_mappings(const recipe& exemplar, const instruction& inst, map<string, const type_tree*>& mappings, const recipe& caller_recipe, bool* error) {
|
||||
|
@ -372,7 +372,7 @@ void accumulate_type_ingredients(const type_tree* exemplar_type, const type_tree
|
|||
curr_refinement_type = new type_tree(*refinement_type->left);
|
||||
}
|
||||
if (!contains_key(mappings, exemplar_type->name)) {
|
||||
trace(9993, "transform") << "adding mapping from " << exemplar_type->name << " to " << to_string(curr_refinement_type) << end();
|
||||
trace(103, "transform") << "adding mapping from " << exemplar_type->name << " to " << to_string(curr_refinement_type) << end();
|
||||
put(mappings, exemplar_type->name, new type_tree(*curr_refinement_type));
|
||||
}
|
||||
else {
|
||||
|
@ -399,16 +399,16 @@ void accumulate_type_ingredients(const type_tree* exemplar_type, const type_tree
|
|||
void replace_type_ingredients(recipe& new_recipe, const map<string, const type_tree*>& mappings) {
|
||||
// update its header
|
||||
if (mappings.empty()) return;
|
||||
trace(9993, "transform") << "replacing in recipe header ingredients" << end();
|
||||
trace(103, "transform") << "replacing in recipe header ingredients" << end();
|
||||
for (int i = 0; i < SIZE(new_recipe.ingredients); ++i)
|
||||
replace_type_ingredients(new_recipe.ingredients.at(i), mappings, new_recipe);
|
||||
trace(9993, "transform") << "replacing in recipe header products" << end();
|
||||
trace(103, "transform") << "replacing in recipe header products" << end();
|
||||
for (int i = 0; i < SIZE(new_recipe.products); ++i)
|
||||
replace_type_ingredients(new_recipe.products.at(i), mappings, new_recipe);
|
||||
// update its body
|
||||
for (int i = 0; i < SIZE(new_recipe.steps); ++i) {
|
||||
instruction& inst = new_recipe.steps.at(i);
|
||||
trace(9993, "transform") << "replacing in instruction '" << to_string(inst) << "'" << end();
|
||||
trace(103, "transform") << "replacing in instruction '" << to_string(inst) << "'" << end();
|
||||
for (int j = 0; j < SIZE(inst.ingredients); ++j)
|
||||
replace_type_ingredients(inst.ingredients.at(j), mappings, new_recipe);
|
||||
for (int j = 0; j < SIZE(inst.products); ++j)
|
||||
|
@ -425,7 +425,7 @@ void replace_type_ingredients(recipe& new_recipe, const map<string, const type_t
|
|||
|
||||
void replace_type_ingredients(reagent& x, const map<string, const type_tree*>& mappings, const recipe& caller) {
|
||||
string before = to_string(x);
|
||||
trace(9993, "transform") << "replacing in ingredient " << x.original_string << end();
|
||||
trace(103, "transform") << "replacing in ingredient " << x.original_string << end();
|
||||
if (!x.type) {
|
||||
raise << "specializing " << caller.original_name << ": missing type for '" << x.original_string << "'\n" << end();
|
||||
return;
|
||||
|
@ -449,7 +449,7 @@ void replace_type_ingredients(type_tree* type, const map<string, const type_tree
|
|||
if (!contains_key(mappings, type->name))
|
||||
return;
|
||||
const type_tree* replacement = get(mappings, type->name);
|
||||
trace(9993, "transform") << type->name << " => " << names_to_string(replacement) << end();
|
||||
trace(103, "transform") << type->name << " => " << names_to_string(replacement) << end();
|
||||
if (replacement->atom) {
|
||||
if (!contains_key(Type_ordinal, replacement->name)) {
|
||||
// error in program; should be reported elsewhere
|
||||
|
@ -510,7 +510,7 @@ void dump_inspect(const type_tree* x, ostream& out) {
|
|||
}
|
||||
|
||||
void ensure_all_concrete_types(/*const*/ recipe& new_recipe, const recipe& exemplar) {
|
||||
trace(9993, "transform") << "-- ensure all concrete types in recipe " << new_recipe.name << end();
|
||||
trace(103, "transform") << "-- ensure all concrete types in recipe " << new_recipe.name << end();
|
||||
for (int i = 0; i < SIZE(new_recipe.ingredients); ++i)
|
||||
ensure_all_concrete_types(new_recipe.ingredients.at(i), exemplar);
|
||||
for (int i = 0; i < SIZE(new_recipe.products); ++i)
|
||||
|
|
|
@ -344,7 +344,7 @@ void check_immutable_ingredients(const recipe_ordinal r) {
|
|||
// b) we never call 'put' or 'put-index' on it, and
|
||||
// c) any non-primitive recipe calls in the body aren't returning it as a product
|
||||
const recipe& caller = get(Recipe, r);
|
||||
trace(9991, "transform") << "--- check mutability of ingredients in recipe " << caller.name << end();
|
||||
trace(101, "transform") << "--- check mutability of ingredients in recipe " << caller.name << end();
|
||||
if (!caller.has_header) return; // skip check for old-style recipes calling next-ingredient directly
|
||||
for (int i = 0; i < SIZE(caller.ingredients); ++i) {
|
||||
const reagent& current_ingredient = caller.ingredients.at(i);
|
||||
|
|
|
@ -38,7 +38,7 @@ void initialize_transform_rewrite_literal_string_to_text() {
|
|||
|
||||
void rewrite_literal_string_to_text(const recipe_ordinal r) {
|
||||
recipe& caller = get(Recipe, r);
|
||||
trace(9991, "transform") << "--- rewrite literal strings in recipe " << caller.name << end();
|
||||
trace(101, "transform") << "--- rewrite literal strings in recipe " << caller.name << end();
|
||||
if (contains_numeric_locations(caller)) return;
|
||||
vector<instruction> new_instructions;
|
||||
for (int i = 0; i < SIZE(caller.steps); ++i) {
|
||||
|
|
|
@ -59,7 +59,7 @@ Transform.push_back(convert_ingredients_to_text); // idempotent
|
|||
:(code)
|
||||
void convert_ingredients_to_text(const recipe_ordinal r) {
|
||||
recipe& caller = get(Recipe, r);
|
||||
trace(9991, "transform") << "--- convert some ingredients to text in recipe " << caller.name << end();
|
||||
trace(101, "transform") << "--- convert some ingredients to text in recipe " << caller.name << end();
|
||||
// in recipes without named locations, 'stash' is still not extensible
|
||||
if (contains_numeric_locations(caller)) return;
|
||||
convert_ingredients_to_text(caller);
|
||||
|
@ -100,7 +100,7 @@ void convert_ingredients_to_text(recipe& caller) {
|
|||
}
|
||||
}
|
||||
}
|
||||
trace(9993, "transform") << to_string(inst) << end();
|
||||
trace(103, "transform") << to_string(inst) << end();
|
||||
new_instructions.push_back(inst);
|
||||
}
|
||||
caller.steps.swap(new_instructions);
|
||||
|
@ -125,7 +125,7 @@ void convert_ingredient_to_text(reagent& r, vector<instruction>& out, const stri
|
|||
def.ingredients.push_back(r);
|
||||
}
|
||||
def.products.push_back(reagent(tmp_var));
|
||||
trace(9993, "transform") << to_string(def) << end();
|
||||
trace(103, "transform") << to_string(def) << end();
|
||||
out.push_back(def);
|
||||
r.clear(); // reclaim old memory
|
||||
r = reagent(tmp_var);
|
||||
|
|
|
@ -55,7 +55,7 @@ size_t hash_mu_scalar(size_t h, const reagent& r) {
|
|||
|
||||
size_t hash_mu_address(size_t h, reagent& r) {
|
||||
if (r.value == 0) return 0;
|
||||
trace("mem") << "location " << r.value << " is " << no_scientific(get_or_insert(Memory, r.value)) << end();
|
||||
trace(Callstack_depth+1, "mem") << "location " << r.value << " is " << no_scientific(get_or_insert(Memory, r.value)) << end();
|
||||
r.set_value(get_or_insert(Memory, r.value));
|
||||
drop_from_type(r, "address");
|
||||
return hash(h, r);
|
||||
|
|
10
072recipe.cc
10
072recipe.cc
|
@ -79,11 +79,9 @@ case CALL: {
|
|||
:(before "End Primitive Recipe Implementations")
|
||||
case CALL: {
|
||||
// Begin Call
|
||||
if (Trace_stream) {
|
||||
++Trace_stream->callstack_depth;
|
||||
trace("trace") << "indirect 'call': incrementing callstack depth to " << Trace_stream->callstack_depth << end();
|
||||
assert(Trace_stream->callstack_depth < 9000); // 9998-101 plus cushion
|
||||
}
|
||||
trace(Callstack_depth+1, "trace") << "indirect 'call': incrementing callstack depth to " << Callstack_depth << end();
|
||||
++Callstack_depth;
|
||||
assert(Callstack_depth < Max_depth);
|
||||
if (!ingredients.at(0).at(0)) {
|
||||
raise << maybe(current_recipe_name()) << "tried to call empty recipe in '" << to_string(current_instruction()) << "'" << end();
|
||||
break;
|
||||
|
@ -224,7 +222,7 @@ void resolve_indirect_ambiguous_call(const recipe_ordinal r, int index, instruct
|
|||
Transform.push_back(check_indirect_calls_against_header); // idempotent
|
||||
:(code)
|
||||
void check_indirect_calls_against_header(const recipe_ordinal r) {
|
||||
trace(9991, "transform") << "--- type-check 'call' instructions inside recipe " << get(Recipe, r).name << end();
|
||||
trace(101, "transform") << "--- type-check 'call' instructions inside recipe " << get(Recipe, r).name << end();
|
||||
const recipe& caller = get(Recipe, r);
|
||||
for (int i = 0; i < SIZE(caller.steps); ++i) {
|
||||
const instruction& inst = caller.steps.at(i);
|
||||
|
|
|
@ -69,7 +69,7 @@ void run(routine* rr) {
|
|||
skip_to_next_routine();
|
||||
assert(Current_routine);
|
||||
assert(Current_routine->state == RUNNING);
|
||||
trace(9990, "schedule") << current_routine_label() << end();
|
||||
trace(100, "schedule") << current_routine_label() << end();
|
||||
run_current_routine();
|
||||
// Scheduler State Transitions
|
||||
if (Current_routine->completed())
|
||||
|
@ -465,7 +465,7 @@ DISCONTINUED,
|
|||
:(before "End Scheduler State Transitions")
|
||||
if (Current_routine->limit >= 0) {
|
||||
if (Current_routine->limit <= Scheduling_interval) {
|
||||
trace("schedule") << "discontinuing routine " << Current_routine->id << end();
|
||||
trace(100, "schedule") << "discontinuing routine " << Current_routine->id << end();
|
||||
Current_routine->state = DISCONTINUED;
|
||||
Current_routine->limit = 0;
|
||||
}
|
||||
|
|
20
074wait.cc
20
074wait.cc
|
@ -89,13 +89,13 @@ case WAIT_FOR_RESET_THEN_SET: {
|
|||
:(before "End Primitive Recipe Implementations")
|
||||
case WAIT_FOR_RESET_THEN_SET: {
|
||||
int loc = static_cast<int>(ingredients.at(0).at(0));
|
||||
trace(9998, "run") << "wait: *" << loc << " = " << get_or_insert(Memory, loc) << end();
|
||||
trace(Callstack_depth+1, "run") << "wait: *" << loc << " = " << get_or_insert(Memory, loc) << end();
|
||||
if (get_or_insert(Memory, loc) == 0) {
|
||||
trace(9998, "run") << "location " << loc << " is already 0; setting" << end();
|
||||
trace(Callstack_depth+1, "run") << "location " << loc << " is already 0; setting" << end();
|
||||
put(Memory, loc, 1);
|
||||
break;
|
||||
}
|
||||
trace(9998, "run") << "waiting for location " << loc << " to reset" << end();
|
||||
trace(Callstack_depth+1, "run") << "waiting for location " << loc << " to reset" << end();
|
||||
Current_routine->state = WAITING;
|
||||
Current_routine->waiting_on_location = loc;
|
||||
break;
|
||||
|
@ -121,7 +121,7 @@ case RESET: {
|
|||
case RESET: {
|
||||
int loc = static_cast<int>(ingredients.at(0).at(0));
|
||||
put(Memory, loc, 0);
|
||||
trace(9998, "run") << "reset: *" << loc << " = " << get_or_insert(Memory, loc) << end();
|
||||
trace(Callstack_depth+1, "run") << "reset: *" << loc << " = " << get_or_insert(Memory, loc) << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ for (int i = 0; i < SIZE(Routines); ++i) {
|
|||
if (Routines.at(i)->state != WAITING) continue;
|
||||
int loc = Routines.at(i)->waiting_on_location;
|
||||
if (loc && get_or_insert(Memory, loc) == 0) {
|
||||
trace("schedule") << "waking up routine " << Routines.at(i)->id << end();
|
||||
trace(100, "schedule") << "waking up routine " << Routines.at(i)->id << end();
|
||||
put(Memory, loc, 1);
|
||||
Routines.at(i)->state = RUNNING;
|
||||
Routines.at(i)->waiting_on_location = 0;
|
||||
|
@ -212,7 +212,7 @@ case GET_LOCATION: {
|
|||
int result = base_address;
|
||||
for (int i = 0; i < offset; ++i)
|
||||
result += size_of(element_type(base.type, i));
|
||||
trace(9998, "run") << "address to copy is " << result << end();
|
||||
trace(Callstack_depth+1, "run") << "address to copy is " << result << end();
|
||||
products.resize(1);
|
||||
products.at(0).push_back(result);
|
||||
break;
|
||||
|
@ -338,7 +338,7 @@ case WAIT_FOR_ROUTINE: {
|
|||
}
|
||||
Current_routine->state = WAITING;
|
||||
Current_routine->waiting_on_routine = ingredients.at(0).at(0);
|
||||
trace(9998, "run") << "waiting for routine " << ingredients.at(0).at(0) << end();
|
||||
trace(Callstack_depth+1, "run") << "waiting for routine " << ingredients.at(0).at(0) << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -356,7 +356,7 @@ for (int i = 0; i < SIZE(Routines); ++i) {
|
|||
const routine* waitee = Routines.at(j);
|
||||
if (waitee->id == id && waitee->state != RUNNING && waitee->state != WAITING) {
|
||||
// routine is COMPLETED or DISCONTINUED
|
||||
trace("schedule") << "waking up routine " << waiter->id << end();
|
||||
trace(100, "schedule") << "waking up routine " << waiter->id << end();
|
||||
waiter->state = RUNNING;
|
||||
waiter->waiting_on_routine = 0;
|
||||
}
|
||||
|
@ -500,7 +500,7 @@ case WAIT_FOR_ROUTINE_TO_BLOCK: {
|
|||
}
|
||||
Current_routine->state = WAITING;
|
||||
Current_routine->waiting_on_routine_to_block = ingredients.at(0).at(0);
|
||||
trace(9998, "run") << "waiting for routine " << ingredients.at(0).at(0) << " to block" << end();
|
||||
trace(Callstack_depth+1, "run") << "waiting for routine " << ingredients.at(0).at(0) << " to block" << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -516,7 +516,7 @@ for (int i = 0; i < SIZE(Routines); ++i) {
|
|||
const routine* waitee = Routines.at(j);
|
||||
if (waitee->id != id) continue;
|
||||
if (waitee->state != RUNNING || waitee->blocked) {
|
||||
trace("schedule") << "waking up routine " << waiter->id << " because routine " << waitee->id << " is blocked" << end();
|
||||
trace(100, "schedule") << "waking up routine " << waiter->id << " because routine " << waitee->id << " is blocked" << end();
|
||||
waiter->state = RUNNING;
|
||||
waiter->waiting_on_routine_to_block = 0;
|
||||
}
|
||||
|
|
|
@ -132,11 +132,9 @@ case CALL_WITH_CONTINUATION_MARK: {
|
|||
case CALL_WITH_CONTINUATION_MARK: {
|
||||
// like call, but mark the current call as a 'base of continuation' call
|
||||
// before pushing the next one on it
|
||||
if (Trace_stream) {
|
||||
++Trace_stream->callstack_depth;
|
||||
trace("trace") << "delimited continuation; incrementing callstack depth to " << Trace_stream->callstack_depth << end();
|
||||
assert(Trace_stream->callstack_depth < 9000); // 9998-101 plus cushion
|
||||
}
|
||||
trace(Callstack_depth+1, "trace") << "delimited continuation; incrementing callstack depth to " << Callstack_depth << end();
|
||||
++Callstack_depth;
|
||||
assert(Callstack_depth < Max_depth);
|
||||
instruction/*copy*/ caller_instruction = current_instruction();
|
||||
Current_routine->calls.front().continuation_mark_tag = current_instruction().ingredients.at(0).value;
|
||||
Current_routine->calls.push_front(call(ingredients.at(1).at(0)));
|
||||
|
@ -213,13 +211,11 @@ case RETURN_CONTINUATION_UNTIL_MARK: {
|
|||
raise << maybe(current_recipe_name()) << " " << get(Recipe, p->running_recipe).name << '\n' << end();
|
||||
break;
|
||||
}
|
||||
trace("run") << "creating continuation " << Next_delimited_continuation_id << end();
|
||||
trace(Callstack_depth+1, "run") << "creating continuation " << Next_delimited_continuation_id << end();
|
||||
put(Delimited_continuation, Next_delimited_continuation_id, delimited_continuation(Current_routine->calls.begin(), base));
|
||||
while (Current_routine->calls.begin() != base) {
|
||||
if (Trace_stream) {
|
||||
--Trace_stream->callstack_depth;
|
||||
assert(Trace_stream->callstack_depth >= 0);
|
||||
}
|
||||
--Callstack_depth;
|
||||
assert(Callstack_depth >= 0);
|
||||
Current_routine->calls.pop_front();
|
||||
}
|
||||
// return it as the result of the marked call
|
||||
|
@ -243,17 +239,15 @@ call_stack::iterator find_base_of_continuation(call_stack& c, int mark_tag) {
|
|||
if (is_mu_continuation(current_instruction().ingredients.at(0))) {
|
||||
// copy multiple calls on to current call stack
|
||||
assert(scalar(ingredients.at(0)));
|
||||
trace("run") << "calling continuation " << ingredients.at(0).at(0) << end();
|
||||
trace(Callstack_depth+1, "run") << "calling continuation " << ingredients.at(0).at(0) << end();
|
||||
if (!contains_key(Delimited_continuation, ingredients.at(0).at(0)))
|
||||
raise << maybe(current_recipe_name()) << "no such delimited continuation " << current_instruction().ingredients.at(0).original_string << '\n' << end();
|
||||
const call_stack& new_frames = get(Delimited_continuation, ingredients.at(0).at(0)).frames;
|
||||
for (call_stack::const_reverse_iterator p = new_frames.rbegin(); p != new_frames.rend(); ++p)
|
||||
Current_routine->calls.push_front(*p);
|
||||
if (Trace_stream) {
|
||||
Trace_stream->callstack_depth += SIZE(new_frames);
|
||||
trace("trace") << "calling delimited continuation; growing callstack depth to " << Trace_stream->callstack_depth << end();
|
||||
assert(Trace_stream->callstack_depth < 9000); // 9998-101 plus cushion
|
||||
}
|
||||
trace(Callstack_depth+1, "trace") << "calling delimited continuation; growing callstack depth to " << Callstack_depth+SIZE(new_frames) << end();
|
||||
Callstack_depth += SIZE(new_frames);
|
||||
assert(Callstack_depth < Max_depth);
|
||||
// no call housekeeping; continuations don't support next-ingredient
|
||||
copy(/*drop continuation*/++ingredients.begin(), ingredients.end(), inserter(products, products.begin()));
|
||||
break; // record results of resuming 'return-continuation-until-mark' instruction
|
||||
|
|
|
@ -66,7 +66,7 @@ case ASSUME_CONSOLE: {
|
|||
for (int i = 0; i < SIZE(r.steps); ++i) {
|
||||
const instruction& inst = r.steps.at(i);
|
||||
if (inst.name == "left-click") {
|
||||
trace("mem") << "storing 'left-click' event starting at " << Current_routine->alloc << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing 'left-click' event starting at " << Current_routine->alloc << end();
|
||||
put(Memory, curr_address, /*tag for 'touch-event' variant of 'event' exclusive-container*/2);
|
||||
put(Memory, curr_address+/*skip tag*/1+/*offset of 'type' in 'mouse-event'*/0, TB_KEY_MOUSE_LEFT);
|
||||
put(Memory, curr_address+/*skip tag*/1+/*offset of 'row' in 'mouse-event'*/1, to_integer(inst.ingredients.at(0).name));
|
||||
|
@ -74,7 +74,7 @@ case ASSUME_CONSOLE: {
|
|||
curr_address += size_of_event();
|
||||
}
|
||||
else if (inst.name == "press") {
|
||||
trace("mem") << "storing 'press' event starting at " << curr_address << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing 'press' event starting at " << curr_address << end();
|
||||
string key = inst.ingredients.at(0).name;
|
||||
if (is_integer(key))
|
||||
put(Memory, curr_address+1, to_integer(key));
|
||||
|
@ -95,18 +95,18 @@ case ASSUME_CONSOLE: {
|
|||
else {
|
||||
// keyboard input
|
||||
assert(inst.name == "type");
|
||||
trace("mem") << "storing 'type' event starting at " << curr_address << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing 'type' event starting at " << curr_address << end();
|
||||
const string& contents = inst.ingredients.at(0).name;
|
||||
const char* raw_contents = contents.c_str();
|
||||
int num_keyboard_events = unicode_length(contents);
|
||||
int curr = 0;
|
||||
for (int i = 0; i < num_keyboard_events; ++i) {
|
||||
trace("mem") << "storing 'text' tag at " << curr_address << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing 'text' tag at " << curr_address << end();
|
||||
put(Memory, curr_address, /*tag for 'text' variant of 'event' exclusive-container*/0);
|
||||
uint32_t curr_character;
|
||||
assert(curr < SIZE(contents));
|
||||
tb_utf8_char_to_unicode(&curr_character, &raw_contents[curr]);
|
||||
trace("mem") << "storing character " << curr_character << " at " << curr_address+/*skip exclusive container tag*/1 << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing character " << curr_character << " at " << curr_address+/*skip exclusive container tag*/1 << end();
|
||||
put(Memory, curr_address+/*skip exclusive container tag*/1, curr_character);
|
||||
curr += tb_utf8_char_length(raw_contents[curr]);
|
||||
curr_address += size_of_event();
|
||||
|
@ -116,9 +116,9 @@ case ASSUME_CONSOLE: {
|
|||
assert(curr_address == event_data_address+/*skip alloc id*/1+size);
|
||||
// wrap the array of events in a console object
|
||||
int console_address = allocate(size_of_console());
|
||||
trace("mem") << "storing console in " << console_address << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing console in " << console_address << end();
|
||||
put(Memory, CONSOLE+/*skip alloc id*/1, console_address);
|
||||
trace("mem") << "storing console data in " << console_address+/*offset of 'data' in container 'events'*/1 << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing console data in " << console_address+/*offset of 'data' in container 'events'*/1 << end();
|
||||
put(Memory, console_address+/*skip alloc id*/1+/*offset of 'data' in container 'events'*/1+/*skip alloc id of 'data'*/1, event_data_address);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -208,24 +208,24 @@ void construct_resources_object(const map<string, string>& contents) {
|
|||
for (map<string, string>::const_iterator p = contents.begin(); p != contents.end(); ++p) {
|
||||
++curr; // skip alloc id of resource.name
|
||||
put(Memory, curr, new_mu_text(p->first));
|
||||
trace("mem") << "storing file name " << get(Memory, curr) << " in location " << curr << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing file name " << get(Memory, curr) << " in location " << curr << end();
|
||||
++curr;
|
||||
++curr; // skip alloc id of resource.contents
|
||||
put(Memory, curr, new_mu_text(p->second));
|
||||
trace("mem") << "storing file contents " << get(Memory, curr) << " in location " << curr << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing file contents " << get(Memory, curr) << " in location " << curr << end();
|
||||
++curr;
|
||||
}
|
||||
curr = resources_data_address + /*skip alloc id of resources.data*/1;
|
||||
put(Memory, curr, SIZE(contents)); // array length
|
||||
trace("mem") << "storing resources size " << get(Memory, curr) << " in location " << curr << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing resources size " << get(Memory, curr) << " in location " << curr << end();
|
||||
// wrap the resources data in a 'resources' object
|
||||
int resources_address = allocate(size_of_resources());
|
||||
curr = resources_address+/*alloc id*/1+/*offset of 'data' element*/1+/*skip alloc id of 'data' element*/1;
|
||||
put(Memory, curr, resources_data_address);
|
||||
trace("mem") << "storing resources data address " << resources_data_address << " in location " << curr << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing resources data address " << resources_data_address << " in location " << curr << end();
|
||||
// save in product
|
||||
put(Memory, RESOURCES+/*skip alloc id*/1, resources_address);
|
||||
trace("mem") << "storing resources address " << resources_address << " in location " << RESOURCES << end();
|
||||
trace(Callstack_depth+1, "mem") << "storing resources address " << resources_address << " in location " << RESOURCES << end();
|
||||
}
|
||||
|
||||
int size_of_resources() {
|
||||
|
|
|
@ -35,7 +35,7 @@ void check_for_misuse_of_real_hardware(const recipe_ordinal r) {
|
|||
const recipe& caller = get(Recipe, r);
|
||||
if (caller.name == "main") return;
|
||||
if (starts_with(caller.name, "scenario_")) return;
|
||||
trace(9991, "transform") << "--- check if recipe " << caller.name << " has any dependency-injection mistakes" << end();
|
||||
trace(101, "transform") << "--- check if recipe " << caller.name << " has any dependency-injection mistakes" << end();
|
||||
for (int index = 0; index < SIZE(caller.steps); ++index) {
|
||||
const instruction& inst = caller.steps.at(index);
|
||||
if (is_primitive(inst.operation)) continue;
|
||||
|
|
|
@ -91,6 +91,7 @@ Most_recent_products = "";
|
|||
:(before "End Globals")
|
||||
trace_stream* Save_trace_stream = NULL;
|
||||
string Save_trace_file;
|
||||
int Save_callstack_depth = 0;
|
||||
:(code)
|
||||
// reads a string, tries to call it as code (treating it as a test), saving
|
||||
// all errors.
|
||||
|
@ -127,9 +128,9 @@ bool run_interactive(int address) {
|
|||
// now call 'sandbox' which will run 'interactive' in a separate routine,
|
||||
// and wait for it
|
||||
if (Save_trace_stream) {
|
||||
++Save_trace_stream->callstack_depth;
|
||||
trace(9999, "trace") << "run-sandboxed: incrementing callstack depth to " << Save_trace_stream->callstack_depth << end();
|
||||
assert(Save_trace_stream->callstack_depth < 9000); // 9998-101 plus cushion
|
||||
++Save_callstack_depth;
|
||||
trace(Save_callstack_depth+1, "trace") << "run-sandboxed: incrementing callstack depth to " << Save_callstack_depth << end();
|
||||
assert(Save_callstack_depth < Max_depth);
|
||||
}
|
||||
Current_routine->calls.push_front(call(get(Recipe_ordinal, "sandbox")));
|
||||
return true;
|
||||
|
@ -149,6 +150,9 @@ map<string, type_tree*> Type_abbreviations_snapshot_stash;
|
|||
vector<scenario> Scenarios_snapshot_stash;
|
||||
set<string> Scenario_names_snapshot_stash;
|
||||
|
||||
:(before "End Types") //: include in all cleaved compilation units
|
||||
const int App_depth = 1; // where all Mu code will trace to by default
|
||||
|
||||
:(code)
|
||||
void run_code_begin(bool should_stash_snapshots) {
|
||||
// stuff to undo later, in run_code_end()
|
||||
|
@ -159,6 +163,7 @@ void run_code_begin(bool should_stash_snapshots) {
|
|||
if (should_stash_snapshots)
|
||||
stash_snapshots();
|
||||
Save_trace_stream = Trace_stream;
|
||||
Save_callstack_depth = Callstack_depth;
|
||||
Trace_stream = new trace_stream;
|
||||
Trace_stream->collect_depth = App_depth;
|
||||
}
|
||||
|
@ -175,6 +180,7 @@ void run_code_end() {
|
|||
Trace_stream = Save_trace_stream;
|
||||
Save_trace_stream = NULL;
|
||||
Save_trace_file.clear();
|
||||
Save_callstack_depth = 0;
|
||||
Recipe.erase(get(Recipe_ordinal, "interactive")); // keep past sandboxes from inserting errors
|
||||
if (!Recipe_snapshot_stash.empty())
|
||||
unstash_snapshots();
|
||||
|
|
10
999spaces.cc
10
999spaces.cc
|
@ -25,12 +25,10 @@ assert(Next_recipe_ordinal == 1000);
|
|||
|
||||
//:: Depths for tracing
|
||||
//:
|
||||
//: 0 - unused
|
||||
//: 1-100 - app-level trace statements in Mu
|
||||
//: 101-9989 - call-stack statements (mostly label run)
|
||||
assert(Initial_callstack_depth == 101);
|
||||
assert(Max_callstack_depth == 9989);
|
||||
//: 9990-9999 - intra-instruction lines (mostly label mem)
|
||||
//: 0 - errors
|
||||
//: 1-99 - app-level trace statements in Mu
|
||||
//: 100-9999 - call-stack statements (mostly label run)
|
||||
assert(Initial_callstack_depth == 100);
|
||||
|
||||
//:: Summary of transforms and their dependencies
|
||||
//: begin transforms
|
||||
|
|
14
Readme.md
14
Readme.md
|
@ -403,7 +403,19 @@ c) Try running the tests:
|
|||
d) Check out [the programming environment](https://github.com/akkartik/mu/tree/master/edit#readme),
|
||||
the largest app built so far in Mu.
|
||||
|
||||
e) Look at the `build` scripts. Mu's compilation process is itself designed to
|
||||
e) Check out the tracing infrastructure which gives you a maps-like zoomable
|
||||
UI for browsing Mu's traces:
|
||||
|
||||
```shell
|
||||
$ ./mu --trace nqueens.mu # just an example
|
||||
saving trace to 'last_run'
|
||||
$ ./browse_trace/browse_trace last_run
|
||||
# hit 'q' to exit
|
||||
```
|
||||
|
||||
For more details see the [Readme](browse_trace/Readme.md).
|
||||
|
||||
f) Look at the `build` scripts. Mu's compilation process is itself designed to
|
||||
support staged learning. Each of the scripts (`build0`, `build1`, `build2`,
|
||||
etc.) is self-contained and can compile the project by itself. Successive
|
||||
versions add new features and configurability -- and complexity -- to the
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
### A debugging helper that lets you zoom in/out on a trace.
|
||||
|
||||
To try it out, first create an example trace (from the top-level `mu/`
|
||||
directory):
|
||||
|
||||
```shell
|
||||
./mu --trace nqueens.mu
|
||||
```
|
||||
|
||||
This command will save a trace of its execution in a file called `last_run`.
|
||||
The trace consists of a series of lines, each starting with an integer depth
|
||||
and a single-word 'label', followed by a colon and whitespace.
|
||||
|
||||
Now browse this trace:
|
||||
|
||||
```shell
|
||||
./browse_trace/browse_trace last_run
|
||||
```
|
||||
|
||||
You should now find yourself in a UI showing a subsequence of lines from the
|
||||
trace, each line starting with a numeric depth, and ending with a parenthetical
|
||||
count of trace lines hidden after it with greater depths.
|
||||
|
||||
For example, this line:
|
||||
|
||||
```
|
||||
2 app: line1 (30)
|
||||
```
|
||||
|
||||
indicates that it was logged with depth 2, and that 30 following lines have
|
||||
been hidden at a depth greater than 2.
|
||||
|
||||
(As an experiment, hidden counts of 1000 or more are highlighted in red.)
|
||||
|
||||
The UI provides the following hotkeys:
|
||||
|
||||
* `q` or `ctrl-c`: Quit.
|
||||
|
||||
* `Enter`: 'Zoom into' this line. Expand lines hidden after it that were at
|
||||
the next higher level.
|
||||
|
||||
* `Backspace`: 'Zoom out' on a line after zooming in, collapsing lines below
|
||||
expanded by some series of `Enter` commands.
|
||||
|
||||
* `j` or `down-arrow`: Move/scroll cursor down one line.
|
||||
* `k` or `up-arrow`: Move/scroll cursor up one line.
|
||||
* `J` or `ctrl-f` or `page-down`: Scroll cursor down one page.
|
||||
* `K` or `ctrl-b` or `page-up`: Scroll cursor up one page.
|
||||
* `h` or `left-arrow`: Scroll cursor left one character.
|
||||
* `l` or `right-arrow`: Scroll cursor right one character.
|
||||
* `H`: Scroll cursor left one screen-width.
|
||||
* `L`: Scroll cursor right one screen-width.
|
||||
|
||||
* `g` or `home`: Move cursor to start of trace.
|
||||
* `G` or `end`: Move cursor to end of trace.
|
||||
|
||||
* `t`: Move cursor to top line on screen.
|
||||
* `c`: Move cursor to center line on screen.
|
||||
* `b`: Move cursor to bottom line on screen.
|
||||
* `T`: Scroll line at cursor to top of screen.
|
||||
|
||||
* `/`: Search forward for a pattern.
|
||||
* '?': Search backward for a pattern.
|
||||
* `n`: Repeat the previous '/' or '?'.
|
||||
* `N`: Repeat the previous '/' or '?' in the opposite direction.
|
||||
|
||||
After hitting `/`, the mini-editor on the bottom-most line supports the
|
||||
following hotkeys:
|
||||
* ascii characters: add the key to the pattern.
|
||||
* `Enter`: search for the pattern.
|
||||
* `Esc` or `ctrl-c`: cancel the current search, setting the screen back
|
||||
to its state before the search.
|
||||
* `left-arrow`: move cursor left.
|
||||
* `right-arrow`: move cursor right.
|
||||
* `ctrl-a` or `home`: move cursor to start of search pattern.
|
||||
* `ctrl-e` or `end`: move cursor to end of search pattern.
|
||||
* `ctrl-u`: clear search pattern before cursor
|
||||
* `ctrl-k`: clear search pattern at and after cursor
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
cd `dirname $0`
|
||||
./build
|
||||
cd -
|
||||
`dirname $0`/browse_trace_bin $*
|
|
@ -1,105 +1,113 @@
|
|||
//: A debugging helper that lets you zoom in/out on a trace.
|
||||
//: Warning: this tool has zero automated tests.
|
||||
//:
|
||||
//: To try it out, first create an example trace:
|
||||
//: mu --trace nqueens.mu
|
||||
//: Then to browse the trace, which was stored in a file called 'last_run':
|
||||
//: mu browse-trace last_run
|
||||
//:
|
||||
//: You should now find yourself in a UI showing a subsequence of lines from
|
||||
//: the trace, each line starting with a numeric depth, and ending with a
|
||||
//: parenthetical count of trace lines hidden after it with greater depths.
|
||||
//:
|
||||
//: For example, this line:
|
||||
//: 2 app: line1 (30)
|
||||
//: indicates that it was logged with depth 2, and that 30 following lines
|
||||
//: have been hidden at a depth greater than 2.
|
||||
//:
|
||||
//: As an experiment, hidden counts of 1000 or more are in red to highlight
|
||||
//: where you might be particularly interested in expanding.
|
||||
//:
|
||||
//: The UI provides the following hotkeys:
|
||||
//:
|
||||
//: `q` or `ctrl-c`: Quit.
|
||||
//:
|
||||
//: `Enter`: 'Zoom into' this line. Expand some or all of the hidden lines
|
||||
//: at the next higher level, updating parenthetical counts of hidden lines.
|
||||
//:
|
||||
//: `Backspace`: 'Zoom out' on a line after zooming in, collapsing expanded
|
||||
//: lines below by some series of <Enter> commands.
|
||||
//:
|
||||
//: `j` or `down-arrow`: Move/scroll cursor down one line.
|
||||
//: `k` or `up-arrow`: Move/scroll cursor up one line.
|
||||
//: `J` or `ctrl-f` or `page-down`: Scroll cursor down one page.
|
||||
//: `K` or `ctrl-b` or `page-up`: Scroll cursor up one page.
|
||||
//: `h` or `left-arrow`: Scroll cursor left one character.
|
||||
//: `l` or `right-arrow`: Scroll cursor right one character.
|
||||
//: `H`: Scroll cursor left one screen-width.
|
||||
//: `L`: Scroll cursor right one screen-width.
|
||||
//:
|
||||
//: `g` or `home`: Move cursor to start of trace.
|
||||
//: `G` or `end`: Move cursor to end of trace.
|
||||
//:
|
||||
//: `t`: Move cursor to top line on screen.
|
||||
//: `c`: Move cursor to center line on screen.
|
||||
//: `b`: Move cursor to bottom line on screen.
|
||||
//: `T`: Scroll line at cursor to top of screen.
|
||||
//:
|
||||
//: `/`: Search forward for a pattern.
|
||||
//: '?': Search backward for a pattern.
|
||||
//: `n`: Repeat the previous '/' or '?'.
|
||||
//: `N`: Repeat the previous '/' or '?' in the opposite direction.
|
||||
//:
|
||||
//: After hitting `/`, a small editor on the bottom-most line supports the
|
||||
//: following hotkeys:
|
||||
//: ascii characters: add the key to the pattern.
|
||||
//: `Enter`: search for the pattern.
|
||||
//: `Esc` or `ctrl-c`: cancel the current search, setting the screen back
|
||||
//: to its state before the search.
|
||||
//: `left-arrow`: move cursor left.
|
||||
//: `right-arrow`: move cursor right.
|
||||
//: `ctrl-a` or `home`: move cursor to start of search pattern.
|
||||
//: `ctrl-e` or `end`: move cursor to end of search pattern.
|
||||
//: `ctrl-u`: clear search pattern before cursor
|
||||
//: `ctrl-k`: clear search pattern at and after cursor
|
||||
// Warning: zero automated tests
|
||||
|
||||
:(before "End Primitive Recipe Declarations")
|
||||
_BROWSE_TRACE,
|
||||
:(before "End Primitive Recipe Numbers")
|
||||
put(Recipe_ordinal, "$browse-trace", _BROWSE_TRACE);
|
||||
:(before "End Primitive Recipe Checks")
|
||||
case _BROWSE_TRACE: {
|
||||
break;
|
||||
// Includes
|
||||
#include "../termbox/termbox.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#define SIZE(X) (assert((X).size() < (1LL<<(sizeof(int)*8-2))), static_cast<int>((X).size()))
|
||||
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
using std::istream;
|
||||
using std::ostream;
|
||||
using std::iostream;
|
||||
using std::cin;
|
||||
using std::cout;
|
||||
using std::cerr;
|
||||
#include <iomanip>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
using std::string;
|
||||
#include <vector>
|
||||
using std::vector;
|
||||
#include <set>
|
||||
using std::set;
|
||||
#include <sstream>
|
||||
using std::ostringstream;
|
||||
#include <fstream>
|
||||
using std::ifstream;
|
||||
using std::ofstream;
|
||||
#include <map>
|
||||
using std::map;
|
||||
#include <utility>
|
||||
using std::pair;
|
||||
// End Includes
|
||||
|
||||
// Types
|
||||
struct trace_line {
|
||||
string contents;
|
||||
string label;
|
||||
int depth; // 0 is 'sea level'; positive integers are progressively 'deeper' and lower level
|
||||
trace_line(string c, string l, int d) {
|
||||
contents = c;
|
||||
label = l;
|
||||
depth = d;
|
||||
}
|
||||
};
|
||||
|
||||
struct trace_stream {
|
||||
vector<trace_line> past_lines;
|
||||
};
|
||||
|
||||
enum search_direction { FORWARD, BACKWARD };
|
||||
// End Types
|
||||
|
||||
// Function prototypes are auto-generated in the 'build*' scripts; define your
|
||||
// functions in any order. Just be sure to declare each function header all on
|
||||
// one line, ending with the '{'. Our auto-generation scripts are too minimal
|
||||
// and simple-minded to handle anything else.
|
||||
#include "function_list" // by convention, files ending with '_list' are auto-generated
|
||||
|
||||
// from http://stackoverflow.com/questions/152643/idiomatic-c-for-reading-from-a-const-map
|
||||
template<typename T> typename T::mapped_type& get(T& map, typename T::key_type const& key) {
|
||||
typename T::iterator iter(map.find(key));
|
||||
assert(iter != map.end());
|
||||
return iter->second;
|
||||
}
|
||||
:(before "End Primitive Recipe Implementations")
|
||||
case _BROWSE_TRACE: {
|
||||
start_trace_browser();
|
||||
break;
|
||||
template<typename T> typename T::mapped_type const& get(const T& map, typename T::key_type const& key) {
|
||||
typename T::const_iterator iter(map.find(key));
|
||||
assert(iter != map.end());
|
||||
return iter->second;
|
||||
}
|
||||
template<typename T> typename T::mapped_type const& put(T& map, typename T::key_type const& key, typename T::mapped_type const& value) {
|
||||
// map[key] requires mapped_type to have a zero-arg (default) constructor
|
||||
map.insert(std::make_pair(key, value)).first->second = value;
|
||||
return value;
|
||||
}
|
||||
template<typename T> bool contains_key(T& map, typename T::key_type const& key) {
|
||||
return map.find(key) != map.end();
|
||||
}
|
||||
template<typename T> typename T::mapped_type& get_or_insert(T& map, typename T::key_type const& key) {
|
||||
return map[key];
|
||||
}
|
||||
|
||||
//: browse a trace loaded from a file
|
||||
:(after "Commandline Parsing")
|
||||
if (argc == 3 && is_equal(argv[1], "browse-trace")) {
|
||||
load_trace(argv[2]);
|
||||
start_trace_browser();
|
||||
return 0;
|
||||
}
|
||||
// Globals
|
||||
trace_stream* Trace_stream = NULL;
|
||||
|
||||
:(before "End Globals")
|
||||
ofstream Trace_file;
|
||||
int Display_row = 0;
|
||||
int Display_column = 0;
|
||||
set<int> Visible;
|
||||
int Top_of_screen = 0;
|
||||
int Left_of_screen = 0;
|
||||
int Last_printed_row = 0;
|
||||
map<int, int> Trace_index; // screen row -> trace index
|
||||
string Current_search_pattern = "";
|
||||
:(before "End Types")
|
||||
enum search_direction { FORWARD, BACKWARD };
|
||||
:(before "End Globals")
|
||||
search_direction Current_search_direction = FORWARD;
|
||||
|
||||
:(code)
|
||||
void start_trace_browser() {
|
||||
if (!Trace_stream) return;
|
||||
string Current_search_pattern = "";
|
||||
search_direction Current_search_direction = FORWARD;
|
||||
// End Globals
|
||||
|
||||
bool has_data(istream& in) {
|
||||
return in && !in.eof();
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 2) {
|
||||
cerr << "Usage: browse_trace <trace file>\n";
|
||||
return 1;
|
||||
}
|
||||
load_trace(argv[1]);
|
||||
if (!Trace_stream) return 1;
|
||||
cerr << "computing min depth to display\n";
|
||||
int min_depth = 9999;
|
||||
for (int i = 0; i < SIZE(Trace_stream->past_lines); ++i) {
|
||||
|
@ -266,6 +274,7 @@ void start_trace_browser() {
|
|||
}
|
||||
}
|
||||
tb_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool start_search_editor(search_direction dir) {
|
||||
|
@ -514,7 +523,7 @@ void load_trace(const char* filename) {
|
|||
if (*--label.end() == ':') label.erase(--label.end());
|
||||
string line;
|
||||
getline(tin, line);
|
||||
Trace_stream->past_lines.push_back(trace_line(depth, label, line));
|
||||
Trace_stream->past_lines.push_back(trace_line(line, label, depth));
|
||||
}
|
||||
cerr << "lines read: " << Trace_stream->past_lines.size() << '\n';
|
||||
}
|
||||
|
@ -526,3 +535,12 @@ int read_key() {
|
|||
} while (event.type != TB_EVENT_KEY);
|
||||
return event.key ? event.key : event.ch;
|
||||
}
|
||||
|
||||
void skip_whitespace_but_not_newline(istream& in) {
|
||||
while (true) {
|
||||
if (!has_data(in)) break;
|
||||
else if (in.peek() == '\n') break;
|
||||
else if (isspace(in.peek())) in.get();
|
||||
else break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
#!/bin/sh
|
||||
# For details on the basic form of this script, see https://notabug.org/akkartik/basic-build.
|
||||
|
||||
set -e # stop immediately on error
|
||||
|
||||
# Some environment variables that can be passed in. For example, to turn off
|
||||
# optimization:
|
||||
# $ CFLAGS=-g ./build2
|
||||
test "$CXX" || export CXX=c++
|
||||
test "$CC" || export CC=cc
|
||||
test "$CFLAGS" || export CFLAGS="-g -O2"
|
||||
export CFLAGS="$CFLAGS -Wall -Wextra -ftrapv -fno-strict-aliasing"
|
||||
|
||||
# there's two mechanisms for fast builds here:
|
||||
# - if a command is quick to run, always run it but update the result only on any change
|
||||
# - otherwise run it only if the output is 'older_than' the inputs
|
||||
#
|
||||
# avoid combining both mechanisms for a single file
|
||||
# otherwise you'll see spurious messages about files being updated
|
||||
# risk: a file may unnecessarily update without changes, causing unnecessary work downstream
|
||||
|
||||
# return 1 if $1 is older than _any_ of the remaining args
|
||||
older_than() {
|
||||
local target=$1
|
||||
shift
|
||||
if [ ! -e $target ]
|
||||
then
|
||||
#? echo "$target doesn't exist"
|
||||
echo "updating $target" >&2
|
||||
return 0 # success
|
||||
fi
|
||||
local f
|
||||
for f in $*
|
||||
do
|
||||
if [ $f -nt $target ]
|
||||
then
|
||||
echo "updating $target" >&2
|
||||
return 0 # success
|
||||
fi
|
||||
done
|
||||
return 1 # failure
|
||||
}
|
||||
|
||||
# redirect to $1, unless it's already identical
|
||||
update() {
|
||||
if [ ! -e $1 ]
|
||||
then
|
||||
cat > $1
|
||||
else
|
||||
cat > $1.tmp
|
||||
diff -q $1 $1.tmp >/dev/null && rm $1.tmp || mv $1.tmp $1
|
||||
fi
|
||||
}
|
||||
|
||||
noisy_cd() {
|
||||
cd $1
|
||||
echo "-- `pwd`" >&2
|
||||
}
|
||||
|
||||
older_than browse_trace_bin browse_trace.cc *_list ../termbox/* && {
|
||||
# missing some deps here
|
||||
noisy_cd ../termbox
|
||||
older_than ../termbox/libtermbox.a ../termbox/*.c && {
|
||||
older_than utf8.o utf8.c && {
|
||||
$CC $CFLAGS -c utf8.c
|
||||
}
|
||||
older_than termbox.o termbox.c termbox.h input.inl output.inl bytebuffer.inl && {
|
||||
$CC $CFLAGS -c termbox.c
|
||||
}
|
||||
older_than libtermbox.a *.o && {
|
||||
ar rcs libtermbox.a *.o
|
||||
}
|
||||
}
|
||||
noisy_cd ../browse_trace
|
||||
|
||||
grep -h "^[^[:space:]#].*) {$" browse_trace.cc |grep -v ":.*(" |sed 's/ {.*/;/' |update function_list
|
||||
# auto-generate list of tests to run
|
||||
grep -h "^\s*void test_" browse_trace.cc |sed 's/^\s*void \(.*\)() {.*/\1,/' |update test_list
|
||||
grep -h "^\s*void test_" browse_trace.cc |sed 's/^\s*void \(.*\)() {.*/"\1",/' |update test_name_list
|
||||
$CXX $CFLAGS browse_trace.cc ../termbox/libtermbox.a -o browse_trace_bin
|
||||
}
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,6 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
set -v
|
||||
rm -rf browse_trace_bin* *_list
|
||||
rm -rf termbox/*.o termbox/libtermbox.a
|
433
subx/003trace.cc
433
subx/003trace.cc
|
@ -13,30 +13,31 @@
|
|||
//: longer valid. In both cases you end up having to reorganize code as well as
|
||||
//: tests, an error-prone activity.
|
||||
//:
|
||||
//: In response, this layer introduces the notion of *domain-driven* testing.
|
||||
//: We focus on the domain of inputs the whole program needs to handle rather
|
||||
//: than the correctness of individual functions. All tests invoke the program
|
||||
//: in a single way: by calling run() with some input. As the program operates
|
||||
//: on the input, it traces out a list of _facts_ deduced about the domain:
|
||||
//: In response, this layer introduces the notion of domain-driven *white-box*
|
||||
//: testing. We focus on the domain of inputs the whole program needs to
|
||||
//: handle rather than the correctness of individual functions. All white-box
|
||||
//: tests (we call them 'scenarios') invoke the program in a single way: by
|
||||
//: calling run() with some input. As the program operates on the input, it
|
||||
//: traces out a list of _facts_ deduced about the domain:
|
||||
//: trace("label") << "fact 1: " << val;
|
||||
//:
|
||||
//: Tests can now check these facts:
|
||||
//: Scenarios can now check these facts:
|
||||
//: :(scenario foo)
|
||||
//: 34 # call run() with this input
|
||||
//: +label: fact 1: 34 # 'run' should have deduced this fact
|
||||
//: -label: fact 1: 35 # the trace should not contain such a fact
|
||||
//:
|
||||
//: Since we never call anything but the run() function directly, we never have
|
||||
//: to rewrite the tests when we reorganize the internals of the program. We
|
||||
//: to rewrite the scenarios when we reorganize the internals of the program. We
|
||||
//: just have to make sure our rewrite deduces the same facts about the domain,
|
||||
//: and that's something we're going to have to do anyway.
|
||||
//:
|
||||
//: To avoid the combinatorial explosion of integration tests, each layer
|
||||
//: mainly logs facts to the trace with a common *label*. All tests in a layer
|
||||
//: tend to check facts with this label. Validating the facts logged with a
|
||||
//: specific label is like calling functions of that layer directly.
|
||||
//: mainly logs facts to the trace with a common *label*. All scenarios in a
|
||||
//: layer tend to check facts with this label. Validating the facts logged
|
||||
//: with a specific label is like calling functions of that layer directly.
|
||||
//:
|
||||
//: To build robust tests, trace facts about your domain rather than details of
|
||||
//: To build robust scenarios, trace facts about your domain rather than details of
|
||||
//: how you computed them.
|
||||
//:
|
||||
//: More details: http://akkartik.name/blog/tracing-tests
|
||||
|
@ -44,158 +45,183 @@
|
|||
//: ---
|
||||
//:
|
||||
//: Between layers and domain-driven testing, programming starts to look like a
|
||||
//: fundamentally different activity. Instead of a) superficial, b) local rules
|
||||
//: on c) code [like say http://blog.bbv.ch/2013/06/05/clean-code-cheat-sheet],
|
||||
//: we allow programmers to engage with the a) deep, b) global structure of the
|
||||
//: c) domain. If you can systematically track discontinuities in the domain,
|
||||
//: you don't care if the code used gotos as long as it passed the tests. If
|
||||
//: tests become more robust to run it becomes easier to try out radically
|
||||
//: different implementations for the same program. If code is super-easy to
|
||||
//: rewrite, it becomes less important what indentation style it uses, or that
|
||||
//: the objects are appropriately encapsulated, or that the functions are
|
||||
//: referentially transparent.
|
||||
//: fundamentally different activity. Instead of focusing on a) superficial,
|
||||
//: b) local rules on c) code [like say http://blog.bbv.ch/2013/06/05/clean-code-cheat-sheet],
|
||||
//: we allow programmers to engage with the a) deep, b) global structure of
|
||||
//: the c) domain. If you can systematically track discontinuities in the
|
||||
//: domain, you don't care if the code used gotos as long as it passed all
|
||||
//: scenarios. If scenarios become more robust to run, it becomes easier to
|
||||
//: try out radically different implementations for the same program. If code
|
||||
//: is super-easy to rewrite, it becomes less important what indentation style
|
||||
//: it uses, or that the objects are appropriately encapsulated, or that the
|
||||
//: functions are referentially transparent.
|
||||
//:
|
||||
//: Instead of plumbing, programming becomes building and gradually refining a
|
||||
//: map of the environment the program must operate under. Whether a program is
|
||||
//: 'correct' at a given point in time is a red herring; what matters is
|
||||
//: avoiding regression by monotonically nailing down the more 'eventful' parts
|
||||
//: of the terrain. It helps readers new and old, and rewards curiosity, to
|
||||
//: organize large programs in self-similar hierarchies of example scenarios
|
||||
//: map of the environment the program must operate under. Whether a program
|
||||
//: is 'correct' at a given point in time is a red herring; what matters is
|
||||
//: avoiding regression by monotonically nailing down the more 'eventful'
|
||||
//: parts of the terrain. It helps readers new and old, and rewards curiosity,
|
||||
//: to organize large programs in self-similar hierarchies of example scenarios
|
||||
//: colocated with the code that makes them work.
|
||||
//:
|
||||
//: "Programming properly should be regarded as an activity by which
|
||||
//: programmers form a mental model, rather than as production of a program."
|
||||
//: -- Peter Naur (http://alistair.cockburn.us/ASD+book+extract%3A+%22Naur,+Ehn,+Musashi%22)
|
||||
|
||||
:(before "End Types")
|
||||
struct trace_line {
|
||||
int depth; // optional field just to help browse traces later
|
||||
string label;
|
||||
string contents;
|
||||
trace_line(string l, string c) :depth(0), label(l), contents(c) {}
|
||||
trace_line(int d, string l, string c) :depth(d), label(l), contents(c) {}
|
||||
};
|
||||
//:: == Core data structures
|
||||
|
||||
:(code)
|
||||
void cleanup_main() {
|
||||
if (!Trace_stream) return;
|
||||
delete Trace_stream;
|
||||
Trace_stream = NULL;
|
||||
}
|
||||
:(before "End One-time Setup")
|
||||
atexit(cleanup_main);
|
||||
:(before "End Globals")
|
||||
trace_stream* Trace_stream = NULL;
|
||||
|
||||
:(before "End Types")
|
||||
// Pre-define some global constants that trace_stream needs to know about.
|
||||
// Since they're in the Types section, they'll be included in any cleaved
|
||||
// compilation units. So no extern linkage.
|
||||
const int Max_depth = 9999;
|
||||
const int Error_depth = 0; // definitely always print errors
|
||||
const int Warn_depth = 1;
|
||||
|
||||
struct trace_stream {
|
||||
vector<trace_line> past_lines;
|
||||
// accumulator for current line
|
||||
ostringstream* curr_stream;
|
||||
string curr_label;
|
||||
int curr_depth;
|
||||
int collect_depth;
|
||||
ofstream null_stream; // never opens a file, so writes silently fail
|
||||
trace_stream() :curr_stream(NULL), curr_depth(Max_depth), collect_depth(Max_depth) {}
|
||||
~trace_stream() { if (curr_stream) delete curr_stream; }
|
||||
// End trace_stream Fields
|
||||
|
||||
ostream& stream(string label) {
|
||||
return stream(Max_depth, label);
|
||||
trace_stream() {
|
||||
// End trace_stream Constructor
|
||||
}
|
||||
|
||||
ostream& stream(int depth, string label) {
|
||||
if (depth > collect_depth) return null_stream;
|
||||
curr_stream = new ostringstream;
|
||||
curr_label = label;
|
||||
curr_depth = depth;
|
||||
(*curr_stream) << std::hex;
|
||||
return *curr_stream;
|
||||
~trace_stream() {
|
||||
// End trace_stream Destructor
|
||||
}
|
||||
|
||||
// be sure to call this before messing with curr_stream or curr_label
|
||||
void newline();
|
||||
// useful for debugging
|
||||
string readable_contents(string label); // empty label = show everything
|
||||
// End trace_stream Methods
|
||||
};
|
||||
|
||||
//:: == Adding to the trace
|
||||
|
||||
//: Top-level method is trace() which can be used like an ostream. Usage:
|
||||
//: trace(depth, label) << ... << end();
|
||||
//: Don't forget the 'end()' to actually append to the trace.
|
||||
:(before "End Includes")
|
||||
// No brackets around the expansion so that it prints nothing if Trace_stream
|
||||
// isn't initialized.
|
||||
#define trace(...) !Trace_stream ? cerr : Trace_stream->stream(__VA_ARGS__)
|
||||
|
||||
:(before "End trace_stream Fields")
|
||||
// accumulator for current trace_line
|
||||
ostringstream* curr_stream;
|
||||
string curr_label;
|
||||
int curr_depth;
|
||||
// other stuff
|
||||
int collect_depth; // avoid tracing lower levels for speed
|
||||
ofstream null_stream; // never opened, so writes to it silently fail
|
||||
|
||||
//: Some constants.
|
||||
:(before "struct trace_stream") // include constants in all cleaved compilation units
|
||||
const int Max_depth = 9999;
|
||||
// Most important traces are printed to the screen by default
|
||||
const int Error_depth = 0;
|
||||
const int Warn_depth = 1;
|
||||
:(before "End Globals")
|
||||
int Hide_errors = false; // if set, don't print errors or warnings to screen
|
||||
int Hide_warnings = false; // if set, don't print warnings to screen
|
||||
:(before "End trace_stream Constructor")
|
||||
curr_stream = NULL;
|
||||
curr_depth = Max_depth;
|
||||
collect_depth = Max_depth;
|
||||
:(before "End Reset")
|
||||
Hide_errors = false;
|
||||
Hide_warnings = false;
|
||||
//: Never dump warnings in scenarios
|
||||
:(before "End Test Setup")
|
||||
Hide_warnings = true;
|
||||
|
||||
:(before "struct trace_stream")
|
||||
struct trace_line {
|
||||
string contents;
|
||||
string label;
|
||||
int depth; // 0 is 'sea level'; positive integers are progressively 'deeper' and lower level
|
||||
trace_line(string c, string l) {
|
||||
contents = c;
|
||||
label = l;
|
||||
depth = 0;
|
||||
}
|
||||
trace_line(string c, string l, int d) {
|
||||
contents = c;
|
||||
label = l;
|
||||
depth = d;
|
||||
}
|
||||
};
|
||||
|
||||
//: Starting a new trace line.
|
||||
:(before "End trace_stream Methods")
|
||||
ostream& stream(string label) {
|
||||
return stream(Max_depth, label);
|
||||
}
|
||||
|
||||
ostream& stream(int depth, string label) {
|
||||
if (depth > collect_depth) return null_stream;
|
||||
curr_stream = new ostringstream;
|
||||
curr_label = label;
|
||||
curr_depth = depth;
|
||||
(*curr_stream) << std::hex; // printing addresses is the common case
|
||||
return *curr_stream;
|
||||
}
|
||||
|
||||
//: End of a trace line; append it to the trace.
|
||||
:(before "End Types")
|
||||
struct end {};
|
||||
:(code)
|
||||
ostream& operator<<(ostream& os, end /*unused*/) {
|
||||
if (Trace_stream) Trace_stream->newline();
|
||||
return os;
|
||||
}
|
||||
|
||||
:(before "End trace_stream Methods")
|
||||
void newline();
|
||||
:(code)
|
||||
void trace_stream::newline() {
|
||||
if (!curr_stream) return;
|
||||
string curr_contents = curr_stream->str();
|
||||
if (!curr_contents.empty()) {
|
||||
past_lines.push_back(trace_line(curr_depth, trim(curr_label), curr_contents)); // preserve indent in contents
|
||||
past_lines.push_back(trace_line(curr_contents, trim(curr_label), curr_depth)); // preserve indent in contents
|
||||
// maybe incrementally dump trace
|
||||
trace_line& t = past_lines.back();
|
||||
if ((!Hide_errors && curr_depth == Error_depth)
|
||||
|| (!Hide_warnings && !Hide_errors && curr_depth == Warn_depth)
|
||||
|| Dump_trace
|
||||
|| (!Dump_label.empty() && curr_label == Dump_label))
|
||||
cerr << curr_label << ": " << curr_contents << '\n';
|
||||
|| (!Hide_warnings && !Hide_errors && curr_depth == Warn_depth)) {
|
||||
cerr << std::setw(4) << t.depth << ' ' << t.label << ": " << t.contents << '\n';
|
||||
}
|
||||
// End trace Commit
|
||||
}
|
||||
|
||||
// clean up
|
||||
delete curr_stream;
|
||||
curr_stream = NULL;
|
||||
curr_label.clear();
|
||||
curr_depth = Max_depth;
|
||||
}
|
||||
|
||||
string trace_stream::readable_contents(string label) {
|
||||
ostringstream output;
|
||||
label = trim(label);
|
||||
for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p)
|
||||
if (label.empty() || label == p->label)
|
||||
output << std::setw(4) << p->depth << ' ' << p->label << ": " << p->contents << '\n';
|
||||
return output.str();
|
||||
}
|
||||
|
||||
:(before "End Globals")
|
||||
trace_stream* Trace_stream = NULL;
|
||||
int Trace_errors = 0; // used only when Trace_stream is NULL
|
||||
|
||||
//: commandline flag to print trace incrementally to stderr
|
||||
:(before "End Globals")
|
||||
bool Flag_dump_trace = false;
|
||||
:(before "End Commandline Options(*arg)")
|
||||
else if (is_equal(*arg, "--trace")) {
|
||||
Flag_dump_trace = true;
|
||||
}
|
||||
|
||||
:(before "End Globals")
|
||||
bool Hide_errors = false; // if set, don't print even error trace lines to screen
|
||||
bool Hide_warnings = false; // if set, don't print warnings to screen
|
||||
bool Dump_trace = false; // if set, print trace lines to screen
|
||||
string Dump_label = ""; // if set, print trace lines matching a single label to screen
|
||||
:(before "End Reset")
|
||||
Hide_errors = false;
|
||||
Hide_warnings = false;
|
||||
Dump_trace = Flag_dump_trace;
|
||||
Dump_label = "";
|
||||
//: Never dump warnings in scenarios
|
||||
:(before "End Test Setup")
|
||||
Hide_warnings = true;
|
||||
//:: == Initializing the trace in scenarios
|
||||
|
||||
:(before "End Includes")
|
||||
#define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream;
|
||||
#define START_TRACING_UNTIL_END_OF_SCOPE lease_tracer leased_tracer;
|
||||
:(before "End Test Setup")
|
||||
START_TRACING_UNTIL_END_OF_SCOPE
|
||||
|
||||
// Top-level helper. IMPORTANT: can't nest
|
||||
#define trace(...) !Trace_stream ? cerr /*print nothing*/ : Trace_stream->stream(__VA_ARGS__)
|
||||
//: Trace_stream is a resource, lease_tracer uses RAII to manage it.
|
||||
:(before "End Types")
|
||||
struct lease_tracer {
|
||||
lease_tracer();
|
||||
~lease_tracer();
|
||||
};
|
||||
:(code)
|
||||
lease_tracer::lease_tracer() { Trace_stream = new trace_stream; }
|
||||
lease_tracer::~lease_tracer() {
|
||||
delete Trace_stream;
|
||||
Trace_stream = NULL;
|
||||
}
|
||||
|
||||
// Just for debugging; 'git log' should never show any calls to 'dbg'.
|
||||
#define dbg trace(0, "a")
|
||||
#define DUMP(label) if (Trace_stream) cerr << Trace_stream->readable_contents(label);
|
||||
//:: == Errors and warnings using traces
|
||||
|
||||
// Errors and warnings are special layers.
|
||||
:(before "End Includes")
|
||||
#define raise (!Trace_stream ? (++Trace_errors,cerr) /*do print*/ : Trace_stream->stream(Error_depth, "error"))
|
||||
#define warn (!Trace_stream ? (++Trace_errors,cerr) /*do print*/ : Trace_stream->stream(Warn_depth, "warn"))
|
||||
// If we aren't yet sure how to deal with some corner case, use assert_for_now
|
||||
// to indicate that it isn't an inviolable invariant.
|
||||
#define assert_for_now assert
|
||||
#define raise_for_now raise
|
||||
|
||||
// Inside tests, fail any tests that displayed (unexpected) errors.
|
||||
// Expected errors in tests should always be hidden and silently checked for.
|
||||
:(before "End Globals")
|
||||
int Trace_errors = 0; // used only when Trace_stream is NULL
|
||||
|
||||
// Fail scenarios that displayed (unexpected) errors.
|
||||
// Expected errors should always be hidden and silently checked for.
|
||||
:(before "End Test Teardown")
|
||||
if (Passed && !Hide_errors && trace_contains_errors()) {
|
||||
Passed = false;
|
||||
|
@ -205,41 +231,33 @@ bool trace_contains_errors() {
|
|||
return Trace_errors > 0 || trace_count("error") > 0;
|
||||
}
|
||||
|
||||
:(before "End Types")
|
||||
struct end {};
|
||||
:(code)
|
||||
ostream& operator<<(ostream& os, end /*unused*/) {
|
||||
if (Trace_stream) Trace_stream->newline();
|
||||
return os;
|
||||
}
|
||||
|
||||
// Trace_stream is a resource, lease_tracer uses RAII to manage it.
|
||||
:(before "End Types")
|
||||
struct lease_tracer {
|
||||
lease_tracer();
|
||||
~lease_tracer();
|
||||
};
|
||||
:(code)
|
||||
lease_tracer::lease_tracer() { Trace_stream = new trace_stream; }
|
||||
lease_tracer::~lease_tracer() {
|
||||
delete Trace_stream, Trace_stream = NULL;
|
||||
}
|
||||
:(before "End Includes")
|
||||
#define START_TRACING_UNTIL_END_OF_SCOPE lease_tracer leased_tracer;
|
||||
:(before "End Test Setup")
|
||||
START_TRACING_UNTIL_END_OF_SCOPE
|
||||
// If we aren't yet sure how to deal with some corner case, use assert_for_now
|
||||
// to indicate that it isn't an inviolable invariant.
|
||||
#define assert_for_now assert
|
||||
#define raise_for_now raise
|
||||
|
||||
//:: == Other assertions on traces
|
||||
//: Primitives:
|
||||
//: - CHECK_TRACE_CONTENTS(lines)
|
||||
//: Assert that the trace contains the given lines (separated by newlines)
|
||||
//: in order. There can be other intervening lines between them.
|
||||
//: - CHECK_TRACE_DOESNT_CONTAIN(line)
|
||||
//: - CHECK_TRACE_DOESNT_CONTAIN(label, contents)
|
||||
//: Assert that the trace doesn't contain the given (single) line.
|
||||
//: - CHECK_TRACE_COUNT(label, count)
|
||||
//: Assert that the trace contains exactly 'count' lines with the given
|
||||
//: 'label'.
|
||||
//: - CHECK_TRACE_CONTAINS_ERRORS()
|
||||
//: - CHECK_TRACE_DOESNT_CONTAIN_ERRORS()
|
||||
//: - trace_count_prefix(label, prefix)
|
||||
//: Count the number of trace lines with the given 'label' that start with
|
||||
//: the given 'prefix'.
|
||||
|
||||
:(before "End Includes")
|
||||
#define CHECK_TRACE_CONTENTS(...) check_trace_contents(__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
|
||||
|
||||
#define CHECK_TRACE_CONTAINS_ERRORS() CHECK(trace_contains_errors())
|
||||
#define CHECK_TRACE_DOESNT_CONTAIN_ERRORS() \
|
||||
if (Passed && trace_contains_errors()) { \
|
||||
cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \
|
||||
DUMP("error"); \
|
||||
Passed = false; \
|
||||
return; \
|
||||
}
|
||||
#define CHECK_TRACE_DOESNT_CONTAIN(...) CHECK(trace_doesnt_contain(__VA_ARGS__))
|
||||
|
||||
#define CHECK_TRACE_COUNT(label, count) \
|
||||
if (Passed && trace_count(label) != (count)) { \
|
||||
|
@ -250,7 +268,17 @@ START_TRACING_UNTIL_END_OF_SCOPE
|
|||
return; /* Currently we stop at the very first failure. */ \
|
||||
}
|
||||
|
||||
#define CHECK_TRACE_DOESNT_CONTAIN(...) CHECK(trace_doesnt_contain(__VA_ARGS__))
|
||||
#define CHECK_TRACE_CONTAINS_ERRORS() CHECK(trace_contains_errors())
|
||||
#define CHECK_TRACE_DOESNT_CONTAIN_ERRORS() \
|
||||
if (Passed && trace_contains_errors()) { \
|
||||
cerr << "\nF - " << __FUNCTION__ << "(" << __FILE__ << ":" << __LINE__ << "): unexpected errors\n"; \
|
||||
DUMP("error"); \
|
||||
Passed = false; \
|
||||
return; \
|
||||
}
|
||||
|
||||
// Allow scenarios to ignore trace lines generated during setup.
|
||||
#define CLEAR_TRACE delete Trace_stream, Trace_stream = new trace_stream
|
||||
|
||||
:(code)
|
||||
bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expected) {
|
||||
|
@ -285,25 +313,13 @@ bool check_trace_contents(string FUNCTION, string FILE, int LINE, string expecte
|
|||
return false;
|
||||
}
|
||||
|
||||
void split_label_contents(const string& s, string* label, string* contents) {
|
||||
static const string delim(": ");
|
||||
size_t pos = s.find(delim);
|
||||
if (pos == string::npos) {
|
||||
*label = "";
|
||||
*contents = trim(s);
|
||||
bool trace_doesnt_contain(string expected) {
|
||||
vector<string> tmp = split_first(expected, ": ");
|
||||
if (SIZE(tmp) == 1) {
|
||||
raise << expected << ": missing label or contents in trace line\n" << end();
|
||||
assert(false);
|
||||
}
|
||||
else {
|
||||
*label = trim(s.substr(0, pos));
|
||||
*contents = trim(s.substr(pos+SIZE(delim)));
|
||||
}
|
||||
}
|
||||
|
||||
bool line_exists_anywhere(const string& label, const string& contents) {
|
||||
for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
|
||||
if (label != p->label) continue;
|
||||
if (contents == trim(p->contents)) return true;
|
||||
}
|
||||
return false;
|
||||
return trace_count(tmp.at(0), tmp.at(1)) == 0;
|
||||
}
|
||||
|
||||
int trace_count(string label) {
|
||||
|
@ -334,17 +350,25 @@ int trace_count_prefix(string label, string prefix) {
|
|||
return result;
|
||||
}
|
||||
|
||||
bool trace_doesnt_contain(string label, string line) {
|
||||
return trace_count(label, line) == 0;
|
||||
void split_label_contents(const string& s, string* label, string* contents) {
|
||||
static const string delim(": ");
|
||||
size_t pos = s.find(delim);
|
||||
if (pos == string::npos) {
|
||||
*label = "";
|
||||
*contents = trim(s);
|
||||
}
|
||||
else {
|
||||
*label = trim(s.substr(0, pos));
|
||||
*contents = trim(s.substr(pos+SIZE(delim)));
|
||||
}
|
||||
}
|
||||
|
||||
bool trace_doesnt_contain(string expected) {
|
||||
vector<string> tmp = split_first(expected, ": ");
|
||||
if (SIZE(tmp) == 1) {
|
||||
raise << expected << ": missing label or contents in trace line\n" << end();
|
||||
assert(false);
|
||||
bool line_exists_anywhere(const string& label, const string& contents) {
|
||||
for (vector<trace_line>::iterator p = Trace_stream->past_lines.begin(); p != Trace_stream->past_lines.end(); ++p) {
|
||||
if (label != p->label) continue;
|
||||
if (contents == trim(p->contents)) return true;
|
||||
}
|
||||
return trace_doesnt_contain(tmp.at(0), tmp.at(1));
|
||||
return false;
|
||||
}
|
||||
|
||||
vector<string> split(string s, string delim) {
|
||||
|
@ -371,6 +395,53 @@ vector<string> split_first(string s, string delim) {
|
|||
return result;
|
||||
}
|
||||
|
||||
//:: == Helpers for debugging using traces
|
||||
|
||||
:(before "End Includes")
|
||||
// To debug why a scenario is failing, dump its trace using '?'.
|
||||
#define DUMP(label) if (Trace_stream) cerr << Trace_stream->readable_contents(label);
|
||||
|
||||
// To add temporary prints to the trace, use 'dbg'.
|
||||
// `git log` should never show any calls to 'dbg'.
|
||||
#define dbg trace(0, "a")
|
||||
|
||||
//: Dump the entire trace to file where it can be browsed offline.
|
||||
//: Dump the trace as it happens; that way you get something even if the
|
||||
//: program crashes.
|
||||
|
||||
:(before "End Globals")
|
||||
ofstream Trace_file;
|
||||
:(before "End Commandline Options(*arg)")
|
||||
else if (is_equal(*arg, "--trace")) {
|
||||
cerr << "saving trace to 'last_run'\n";
|
||||
Trace_file.open("last_run");
|
||||
}
|
||||
:(before "End trace Commit")
|
||||
if (Trace_file) {
|
||||
Trace_file << std::setw(4) << t.depth << ' ' << t.label << ": " << t.contents << '\n';
|
||||
}
|
||||
:(before "End One-time Setup")
|
||||
atexit(cleanup_main);
|
||||
:(code)
|
||||
void cleanup_main() {
|
||||
if (Trace_file) Trace_file.close();
|
||||
// End cleanup_main
|
||||
}
|
||||
|
||||
:(before "End trace_stream Methods")
|
||||
string readable_contents(string label) {
|
||||
string trim(const string& s); // prototype
|
||||
ostringstream output;
|
||||
label = trim(label);
|
||||
for (vector<trace_line>::iterator p = past_lines.begin(); p != past_lines.end(); ++p)
|
||||
if (label.empty() || label == p->label)
|
||||
output << std::setw(4) << p->depth << ' ' << p->label << ": " << p->contents << '\n';
|
||||
return output.str();
|
||||
}
|
||||
|
||||
//: Miscellaneous helpers.
|
||||
|
||||
:(code)
|
||||
string trim(const string& s) {
|
||||
string::const_iterator first = s.begin();
|
||||
while (first != s.end() && isspace(*first))
|
||||
|
|
|
@ -92,7 +92,7 @@ SF = ZF = OF = false;
|
|||
/* arg1 and arg2 must be signed */ \
|
||||
int64_t tmp = arg1 op arg2; \
|
||||
arg1 = arg1 op arg2; \
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << arg1 << end(); \
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << arg1 << end(); \
|
||||
SF = (arg1 < 0); \
|
||||
ZF = (arg1 == 0); \
|
||||
OF = (arg1 != tmp); \
|
||||
|
@ -103,7 +103,7 @@ SF = ZF = OF = false;
|
|||
#define BINARY_BITWISE_OP(op, arg1, arg2) { \
|
||||
/* arg1 and arg2 must be unsigned */ \
|
||||
arg1 = arg1 op arg2; \
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << arg1 << end(); \
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << arg1 << end(); \
|
||||
SF = (arg1 >> 31); \
|
||||
ZF = (arg1 == 0); \
|
||||
OF = false; \
|
||||
|
@ -296,13 +296,13 @@ inline bool already_allocated(uint32_t addr) {
|
|||
void run_one_instruction() {
|
||||
uint8_t op=0, op2=0, op3=0;
|
||||
// Run One Instruction
|
||||
if (Dump_trace) {
|
||||
if (Trace_file) {
|
||||
dump_registers();
|
||||
// End Dump Info for Instruction
|
||||
}
|
||||
trace(90, "run") << "inst: 0x" << HEXWORD << EIP << end();
|
||||
uint32_t inst_start_address = EIP;
|
||||
op = next();
|
||||
trace(90, "run") << "opcode: " << HEXBYTE << NUM(op) << end();
|
||||
trace(Callstack_depth, "run") << "0x" << HEXWORD << inst_start_address << " opcode: " << HEXBYTE << NUM(op) << (op == 0xe8 ? "/call" : "") << end();
|
||||
switch (op) {
|
||||
case 0xf4: // hlt
|
||||
EIP = End_of_program;
|
||||
|
@ -372,7 +372,7 @@ void dump_registers() {
|
|||
out << " " << i << ": " << std::hex << std::setw(8) << std::setfill('_') << Reg[i].u;
|
||||
}
|
||||
out << " -- SF: " << SF << "; ZF: " << ZF << "; OF: " << OF;
|
||||
trace(90, "run") << out.str() << end();
|
||||
trace(Callstack_depth+1, "run") << out.str() << end();
|
||||
}
|
||||
|
||||
//: start tracking supported opcodes
|
||||
|
@ -409,6 +409,24 @@ if (key == "opcodes") {
|
|||
:(before "End Help Contents")
|
||||
cerr << " opcodes\n";
|
||||
|
||||
//: Helpers for managing trace depths
|
||||
//:
|
||||
//: We're going to use trace depths primarily to segment code running at
|
||||
//: different frames of the call stack. This will make it easy for the trace
|
||||
//: browser to collapse over entire calls.
|
||||
//:
|
||||
//: Errors will be at depth 0.
|
||||
//: Warnings will be at depth 1.
|
||||
//: SubX instructions will occupy depth 2 and up to Max_depth, organized by
|
||||
//: stack frames. Each instruction's internal details will be one level deeper
|
||||
//: than its 'main' depth. So 'call' instruction details will be at the same
|
||||
//: depth as the instructions of the function it calls.
|
||||
:(before "End Globals")
|
||||
extern const int Initial_callstack_depth = 2;
|
||||
int Callstack_depth = Initial_callstack_depth;
|
||||
:(before "End Reset")
|
||||
Callstack_depth = Initial_callstack_depth;
|
||||
|
||||
:(before "End Includes")
|
||||
#include <iomanip>
|
||||
#define HEXBYTE std::hex << std::setw(2) << std::setfill('0')
|
||||
|
|
|
@ -127,7 +127,6 @@ struct word {
|
|||
:(code)
|
||||
void parse(istream& fin, program& out) {
|
||||
vector<line> l;
|
||||
trace(99, "parse") << "begin" << end();
|
||||
while (has_data(fin)) {
|
||||
string line_data;
|
||||
line curr;
|
||||
|
@ -151,7 +150,7 @@ void parse(istream& fin, program& out) {
|
|||
s.start = parse_int(segment_title);
|
||||
sanity_check_program_segment(out, s.start);
|
||||
if (trace_contains_errors()) continue;
|
||||
trace(99, "parse") << "new segment from 0x" << HEXWORD << s.start << end();
|
||||
trace(3, "parse") << "new segment from 0x" << HEXWORD << s.start << end();
|
||||
out.segments.push_back(s);
|
||||
}
|
||||
// End Segment Parsing Special-cases(segment_title)
|
||||
|
@ -226,16 +225,13 @@ vector<transform_fn> Transform;
|
|||
|
||||
:(code)
|
||||
void transform(program& p) {
|
||||
trace(99, "transform") << "begin" << end();
|
||||
for (int t = 0; t < SIZE(Transform); ++t)
|
||||
(*Transform.at(t))(p);
|
||||
trace(99, "transform") << "done" << end();
|
||||
}
|
||||
|
||||
//:: load
|
||||
|
||||
void load(const program& p) {
|
||||
trace(99, "load") << "begin" << end();
|
||||
if (p.segments.empty()) {
|
||||
raise << "no code to run\n" << end();
|
||||
return;
|
||||
|
@ -265,7 +261,6 @@ void load(const program& p) {
|
|||
}
|
||||
EIP = p.segments.at(0).start;
|
||||
// End Initialize EIP
|
||||
trace(99, "load") << "done" << end();
|
||||
}
|
||||
|
||||
uint8_t hex_byte(const string& s) {
|
||||
|
@ -331,7 +326,7 @@ put_new(Name, "05", "add imm32 to EAX (add)");
|
|||
:(before "End Single-Byte Opcodes")
|
||||
case 0x05: { // add imm32 to EAX
|
||||
int32_t arg2 = next32();
|
||||
trace(90, "run") << "add imm32 0x" << HEXWORD << arg2 << " to reg EAX" << end();
|
||||
trace(Callstack_depth+1, "run") << "add imm32 0x" << HEXWORD << arg2 << " to reg EAX" << end();
|
||||
BINARY_ARITHMETIC_OP(+, Reg[EAX].i, arg2);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
assert(argc > 1);
|
||||
if (is_equal(argv[1], "run")) {
|
||||
START_TRACING_UNTIL_END_OF_SCOPE;
|
||||
trace(2, "run") << "=== Starting to run" << end();
|
||||
assert(argc > 2);
|
||||
reset();
|
||||
cerr << std::hex;
|
||||
|
@ -88,8 +89,8 @@ void load_elf_contents(uint8_t* elf_contents, size_t size, int argc, char* argv[
|
|||
|
||||
void push(uint32_t val) {
|
||||
Reg[ESP].u -= 4;
|
||||
trace(90, "run") << "decrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
|
||||
trace(90, "run") << "pushing value 0x" << HEXWORD << val << end();
|
||||
trace(Callstack_depth+1, "run") << "decrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
|
||||
trace(Callstack_depth+1, "run") << "pushing value 0x" << HEXWORD << val << end();
|
||||
write_mem_u32(Reg[ESP].u, val);
|
||||
}
|
||||
|
||||
|
@ -142,12 +143,12 @@ const int ARGV_DATA_SEGMENT = 0x0c000000;
|
|||
:(code)
|
||||
void dump_stack() {
|
||||
ostringstream out;
|
||||
trace(91, "run") << "stack:" << end();
|
||||
trace(Callstack_depth+1, "run") << "stack:" << end();
|
||||
for (uint32_t a = AFTER_STACK-4; a > Reg[ESP].u; a -= 4)
|
||||
trace(91, "run") << " 0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << end();
|
||||
trace(91, "run") << " 0x" << HEXWORD << Reg[ESP].u << " => 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << " <=== ESP" << end();
|
||||
trace(Callstack_depth+1, "run") << " 0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << end();
|
||||
trace(Callstack_depth+1, "run") << " 0x" << HEXWORD << Reg[ESP].u << " => 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << " <=== ESP" << end();
|
||||
for (uint32_t a = Reg[ESP].u-4; a > Reg[ESP].u-40; a -= 4)
|
||||
trace(91, "run") << " 0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << end();
|
||||
trace(Callstack_depth+1, "run") << " 0x" << HEXWORD << a << " => 0x" << HEXWORD << read_mem_u32(a) << end();
|
||||
}
|
||||
|
||||
inline uint32_t u32_in(uint8_t* p) {
|
||||
|
|
|
@ -18,7 +18,7 @@ put_new(Name, "01", "add r32 to rm32 (add)");
|
|||
case 0x01: { // add r32 to r/m32
|
||||
uint8_t modrm = next();
|
||||
uint8_t arg2 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "add " << rname(arg2) << " to r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "add " << rname(arg2) << " to r/m32" << end();
|
||||
int32_t* arg1 = effective_address(modrm);
|
||||
BINARY_ARITHMETIC_OP(+, *arg1, Reg[arg2].i);
|
||||
break;
|
||||
|
@ -34,7 +34,7 @@ int32_t* effective_address(uint8_t modrm) {
|
|||
const uint8_t rm = modrm & 0x7;
|
||||
if (mod == 3) {
|
||||
// mod 3 is just register direct addressing
|
||||
trace(90, "run") << "r/m32 is " << rname(rm) << end();
|
||||
trace(Callstack_depth+1, "run") << "r/m32 is " << rname(rm) << end();
|
||||
return &Reg[rm].i;
|
||||
}
|
||||
return mem_addr_i32(effective_address_number(modrm));
|
||||
|
@ -94,7 +94,7 @@ put_new(Name, "29", "subtract r32 from rm32 (sub)");
|
|||
case 0x29: { // subtract r32 from r/m32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t arg2 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "subtract " << rname(arg2) << " from r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "subtract " << rname(arg2) << " from r/m32" << end();
|
||||
int32_t* arg1 = effective_address(modrm);
|
||||
BINARY_ARITHMETIC_OP(-, *arg1, Reg[arg2].i);
|
||||
break;
|
||||
|
@ -120,17 +120,17 @@ put_new(Name, "f7", "negate/multiply rm32 (with EAX if necessary) depending on s
|
|||
:(before "End Single-Byte Opcodes")
|
||||
case 0xf7: {
|
||||
const uint8_t modrm = next();
|
||||
trace(90, "run") << "operate on r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "operate on r/m32" << end();
|
||||
int32_t* arg1 = effective_address(modrm);
|
||||
const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits
|
||||
switch (subop) {
|
||||
case 4: { // mul unsigned EAX by r/m32
|
||||
trace(90, "run") << "subop: multiply EAX by r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop: multiply EAX by r/m32" << end();
|
||||
const uint64_t result = Reg[EAX].u * static_cast<uint32_t>(*arg1);
|
||||
Reg[EAX].u = result & 0xffffffff;
|
||||
Reg[EDX].u = result >> 32;
|
||||
OF = (Reg[EDX].u != 0);
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[EAX].u << end();
|
||||
break;
|
||||
}
|
||||
// End Op f7 Subops
|
||||
|
@ -161,7 +161,7 @@ put_new(Name_0f, "af", "multiply rm32 into r32 (imul)");
|
|||
case 0xaf: { // multiply r32 into r/m32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t arg2 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "multiply r/m32 into " << rname(arg2) << end();
|
||||
trace(Callstack_depth+1, "run") << "multiply r/m32 into " << rname(arg2) << end();
|
||||
const int32_t* arg1 = effective_address(modrm);
|
||||
BINARY_ARITHMETIC_OP(*, Reg[arg2].i, *arg1);
|
||||
break;
|
||||
|
@ -182,17 +182,17 @@ case 0xaf: { // multiply r32 into r/m32
|
|||
|
||||
:(before "End Op f7 Subops")
|
||||
case 3: { // negate r/m32
|
||||
trace(90, "run") << "subop: negate" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop: negate" << end();
|
||||
// one case that can overflow
|
||||
if (static_cast<uint32_t>(*arg1) == 0x80000000) {
|
||||
trace(90, "run") << "overflow" << end();
|
||||
trace(Callstack_depth+1, "run") << "overflow" << end();
|
||||
SF = true;
|
||||
ZF = false;
|
||||
OF = true;
|
||||
break;
|
||||
}
|
||||
*arg1 = -(*arg1);
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
SF = (*arg1 >> 31);
|
||||
ZF = (*arg1 == 0);
|
||||
OF = false;
|
||||
|
@ -230,12 +230,12 @@ put_new(Name, "d3", "shift rm32 by CL bits depending on subop (sal/sar/shl/shr)"
|
|||
:(before "End Single-Byte Opcodes")
|
||||
case 0xd3: {
|
||||
const uint8_t modrm = next();
|
||||
trace(90, "run") << "operate on r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "operate on r/m32" << end();
|
||||
int32_t* arg1 = effective_address(modrm);
|
||||
const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits
|
||||
switch (subop) {
|
||||
case 4: { // shift left r/m32 by CL
|
||||
trace(90, "run") << "subop: shift left by CL bits" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop: shift left by CL bits" << end();
|
||||
uint8_t count = Reg[ECX].u & 0x1f;
|
||||
// OF is only defined if count is 1
|
||||
if (count == 1) {
|
||||
|
@ -246,7 +246,7 @@ case 0xd3: {
|
|||
*arg1 = (*arg1 << count);
|
||||
ZF = (*arg1 == 0);
|
||||
SF = (*arg1 < 0);
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
break;
|
||||
}
|
||||
// End Op d3 Subops
|
||||
|
@ -273,14 +273,14 @@ case 0xd3: {
|
|||
|
||||
:(before "End Op d3 Subops")
|
||||
case 7: { // shift right r/m32 by CL, preserving sign
|
||||
trace(90, "run") << "subop: shift right by CL bits, while preserving sign" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while preserving sign" << end();
|
||||
uint8_t count = Reg[ECX].u & 0x1f;
|
||||
*arg1 = (*arg1 >> count);
|
||||
ZF = (*arg1 == 0);
|
||||
SF = (*arg1 < 0);
|
||||
// OF is only defined if count is 1
|
||||
if (count == 1) OF = false;
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -326,7 +326,7 @@ case 7: { // shift right r/m32 by CL, preserving sign
|
|||
|
||||
:(before "End Op d3 Subops")
|
||||
case 5: { // shift right r/m32 by CL, preserving sign
|
||||
trace(90, "run") << "subop: shift right by CL bits, while padding zeroes" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while padding zeroes" << end();
|
||||
uint8_t count = Reg[ECX].u & 0x1f;
|
||||
// OF is only defined if count is 1
|
||||
if (count == 1) {
|
||||
|
@ -339,7 +339,7 @@ case 5: { // shift right r/m32 by CL, preserving sign
|
|||
ZF = (*uarg1 == 0);
|
||||
// result is always positive by definition
|
||||
SF = false;
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -388,7 +388,7 @@ put_new(Name, "21", "rm32 = bitwise AND of r32 with rm32 (and)");
|
|||
case 0x21: { // and r32 with r/m32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t arg2 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "and " << rname(arg2) << " with r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "and " << rname(arg2) << " with r/m32" << end();
|
||||
int32_t* arg1 = effective_address(modrm);
|
||||
BINARY_BITWISE_OP(&, *arg1, Reg[arg2].u);
|
||||
break;
|
||||
|
@ -414,7 +414,7 @@ put_new(Name, "09", "rm32 = bitwise OR of r32 with rm32 (or)");
|
|||
case 0x09: { // or r32 with r/m32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t arg2 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "or " << rname(arg2) << " with r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "or " << rname(arg2) << " with r/m32" << end();
|
||||
int32_t* arg1 = effective_address(modrm);
|
||||
BINARY_BITWISE_OP(|, *arg1, Reg[arg2].u);
|
||||
break;
|
||||
|
@ -440,7 +440,7 @@ put_new(Name, "31", "rm32 = bitwise XOR of r32 with rm32 (xor)");
|
|||
case 0x31: { // xor r32 with r/m32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t arg2 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "xor " << rname(arg2) << " with r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "xor " << rname(arg2) << " with r/m32" << end();
|
||||
int32_t* arg1 = effective_address(modrm);
|
||||
BINARY_BITWISE_OP(^, *arg1, Reg[arg2].u);
|
||||
break;
|
||||
|
@ -461,9 +461,9 @@ case 0x31: { // xor r32 with r/m32
|
|||
|
||||
:(before "End Op f7 Subops")
|
||||
case 2: { // not r/m32
|
||||
trace(90, "run") << "subop: not" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop: not" << end();
|
||||
*arg1 = ~(*arg1);
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
SF = (*arg1 >> 31);
|
||||
ZF = (*arg1 == 0);
|
||||
OF = false;
|
||||
|
@ -490,7 +490,7 @@ put_new(Name, "39", "compare: set SF if rm32 < r32 (cmp)");
|
|||
case 0x39: { // set SF if r/m32 < r32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t reg2 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "compare " << rname(reg2) << " with r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "compare " << rname(reg2) << " with r/m32" << end();
|
||||
const int32_t* arg1 = effective_address(modrm);
|
||||
const int32_t arg2 = Reg[reg2].i;
|
||||
const int32_t tmp1 = *arg1 - arg2;
|
||||
|
@ -498,7 +498,7 @@ case 0x39: { // set SF if r/m32 < r32
|
|||
ZF = (tmp1 == 0);
|
||||
const int64_t tmp2 = *arg1 - arg2;
|
||||
OF = (tmp1 != tmp2);
|
||||
trace(90, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
|
||||
trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -543,10 +543,10 @@ put_new(Name, "89", "copy r32 to rm32 (mov)");
|
|||
case 0x89: { // copy r32 to r/m32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t rsrc = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "copy " << rname(rsrc) << " to r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "copy " << rname(rsrc) << " to r/m32" << end();
|
||||
int32_t* dest = effective_address(modrm);
|
||||
*dest = Reg[rsrc].i;
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << *dest << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *dest << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -571,13 +571,13 @@ put_new(Name, "87", "swap the contents of r32 and rm32 (xchg)");
|
|||
case 0x87: { // exchange r32 with r/m32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t reg2 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "exchange " << rname(reg2) << " with r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "exchange " << rname(reg2) << " with r/m32" << end();
|
||||
int32_t* arg1 = effective_address(modrm);
|
||||
const int32_t tmp = *arg1;
|
||||
*arg1 = Reg[reg2].i;
|
||||
Reg[reg2].i = tmp;
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end();
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << " in r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << Reg[reg2].i << " in " << rname(reg2) << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -611,9 +611,9 @@ case 0x45:
|
|||
case 0x46:
|
||||
case 0x47: { // increment r32
|
||||
const uint8_t reg = op & 0x7;
|
||||
trace(90, "run") << "increment " << rname(reg) << end();
|
||||
trace(Callstack_depth+1, "run") << "increment " << rname(reg) << end();
|
||||
++Reg[reg].u;
|
||||
trace(90, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
|
||||
trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -636,10 +636,10 @@ case 0xff: {
|
|||
const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits
|
||||
switch (subop) {
|
||||
case 0: { // increment r/m32
|
||||
trace(90, "run") << "increment r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "increment r/m32" << end();
|
||||
int32_t* arg = effective_address(modrm);
|
||||
++*arg;
|
||||
trace(90, "run") << "storing value 0x" << HEXWORD << *arg << end();
|
||||
trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -681,9 +681,9 @@ case 0x4d:
|
|||
case 0x4e:
|
||||
case 0x4f: { // decrement r32
|
||||
const uint8_t reg = op & 0x7;
|
||||
trace(90, "run") << "decrement " << rname(reg) << end();
|
||||
trace(Callstack_depth+1, "run") << "decrement " << rname(reg) << end();
|
||||
--Reg[reg].u;
|
||||
trace(90, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
|
||||
trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << Reg[reg].u << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -699,10 +699,10 @@ case 0x4f: { // decrement r32
|
|||
|
||||
:(before "End Op ff Subops")
|
||||
case 1: { // decrement r/m32
|
||||
trace(90, "run") << "decrement r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "decrement r/m32" << end();
|
||||
int32_t* arg = effective_address(modrm);
|
||||
--*arg;
|
||||
trace(90, "run") << "storing value 0x" << HEXWORD << *arg << end();
|
||||
trace(Callstack_depth+1, "run") << "storing value 0x" << HEXWORD << *arg << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -738,7 +738,7 @@ case 0x55:
|
|||
case 0x56:
|
||||
case 0x57: { // push r32 to stack
|
||||
uint8_t reg = op & 0x7;
|
||||
trace(90, "run") << "push " << rname(reg) << end();
|
||||
trace(Callstack_depth+1, "run") << "push " << rname(reg) << end();
|
||||
//? cerr << "push: " << NUM(reg) << ": " << Reg[reg].u << " => " << Reg[ESP].u << '\n';
|
||||
push(Reg[reg].u);
|
||||
break;
|
||||
|
@ -779,7 +779,7 @@ case 0x5d:
|
|||
case 0x5e:
|
||||
case 0x5f: { // pop stack into r32
|
||||
const uint8_t reg = op & 0x7;
|
||||
trace(90, "run") << "pop into " << rname(reg) << end();
|
||||
trace(Callstack_depth+1, "run") << "pop into " << rname(reg) << end();
|
||||
//? cerr << "pop from " << Reg[ESP].u << '\n';
|
||||
Reg[reg].u = pop();
|
||||
//? cerr << "=> " << NUM(reg) << ": " << Reg[reg].u << '\n';
|
||||
|
@ -788,8 +788,8 @@ case 0x5f: { // pop stack into r32
|
|||
:(code)
|
||||
uint32_t pop() {
|
||||
const uint32_t result = read_mem_u32(Reg[ESP].u);
|
||||
trace(90, "run") << "popping value 0x" << HEXWORD << result << end();
|
||||
trace(Callstack_depth+1, "run") << "popping value 0x" << HEXWORD << result << end();
|
||||
Reg[ESP].u += 4;
|
||||
trace(90, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
|
||||
trace(Callstack_depth+1, "run") << "incrementing ESP to 0x" << HEXWORD << Reg[ESP].u << end();
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
case 0: // indirect addressing
|
||||
switch (rm) {
|
||||
default: // address in register
|
||||
trace(90, "run") << "effective address is 0x" << HEXWORD << Reg[rm].u << " (" << rname(rm) << ")" << end();
|
||||
trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << Reg[rm].u << " (" << rname(rm) << ")" << end();
|
||||
addr = Reg[rm].u;
|
||||
break;
|
||||
// End Mod 0 Special-cases(addr)
|
||||
|
@ -47,7 +47,7 @@ put_new(Name, "03", "add rm32 to r32 (add)");
|
|||
case 0x03: { // add r/m32 to r32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t arg1 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "add r/m32 to " << rname(arg1) << end();
|
||||
trace(Callstack_depth+1, "run") << "add r/m32 to " << rname(arg1) << end();
|
||||
const int32_t* arg2 = effective_address(modrm);
|
||||
BINARY_ARITHMETIC_OP(+, Reg[arg1].i, *arg2);
|
||||
break;
|
||||
|
@ -90,7 +90,7 @@ put_new(Name, "2b", "subtract rm32 from r32 (sub)");
|
|||
case 0x2b: { // subtract r/m32 from r32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t arg1 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "subtract r/m32 from " << rname(arg1) << end();
|
||||
trace(Callstack_depth+1, "run") << "subtract r/m32 from " << rname(arg1) << end();
|
||||
const int32_t* arg2 = effective_address(modrm);
|
||||
BINARY_ARITHMETIC_OP(-, Reg[arg1].i, *arg2);
|
||||
break;
|
||||
|
@ -133,7 +133,7 @@ ff 00 00 00 # 0xff
|
|||
case 0x23: { // and r/m32 with r32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t arg1 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "and r/m32 with " << rname(arg1) << end();
|
||||
trace(Callstack_depth+1, "run") << "and r/m32 with " << rname(arg1) << end();
|
||||
const int32_t* arg2 = effective_address(modrm);
|
||||
BINARY_BITWISE_OP(&, Reg[arg1].u, *arg2);
|
||||
break;
|
||||
|
@ -176,7 +176,7 @@ put_new(Name, "0b", "r32 = bitwise OR of r32 with rm32 (or)");
|
|||
case 0x0b: { // or r/m32 with r32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t arg1 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "or r/m32 with " << rname(arg1) << end();
|
||||
trace(Callstack_depth+1, "run") << "or r/m32 with " << rname(arg1) << end();
|
||||
const int32_t* arg2 = effective_address(modrm);
|
||||
BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2);
|
||||
break;
|
||||
|
@ -219,7 +219,7 @@ put_new(Name, "33", "r32 = bitwise XOR of r32 with rm32 (xor)");
|
|||
case 0x33: { // xor r/m32 with r32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t arg1 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "xor r/m32 with " << rname(arg1) << end();
|
||||
trace(Callstack_depth+1, "run") << "xor r/m32 with " << rname(arg1) << end();
|
||||
const int32_t* arg2 = effective_address(modrm);
|
||||
BINARY_BITWISE_OP(|, Reg[arg1].u, *arg2);
|
||||
break;
|
||||
|
@ -303,7 +303,7 @@ put_new(Name, "3b", "compare: set SF if r32 < rm32 (cmp)");
|
|||
case 0x3b: { // set SF if r32 < r/m32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t reg1 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "compare r/m32 with " << rname(reg1) << end();
|
||||
trace(Callstack_depth+1, "run") << "compare r/m32 with " << rname(reg1) << end();
|
||||
const int32_t arg1 = Reg[reg1].i;
|
||||
const int32_t* arg2 = effective_address(modrm);
|
||||
const int32_t tmp1 = arg1 - *arg2;
|
||||
|
@ -311,7 +311,7 @@ case 0x3b: { // set SF if r32 < r/m32
|
|||
ZF = (tmp1 == 0);
|
||||
int64_t tmp2 = arg1 - *arg2;
|
||||
OF = (tmp1 != tmp2);
|
||||
trace(90, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
|
||||
trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -375,10 +375,10 @@ af 00 00 00 # 0xaf
|
|||
case 0x8b: { // copy r32 to r/m32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t rdest = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "copy r/m32 to " << rname(rdest) << end();
|
||||
trace(Callstack_depth+1, "run") << "copy r/m32 to " << rname(rdest) << end();
|
||||
const int32_t* src = effective_address(modrm);
|
||||
Reg[rdest].i = *src;
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << *src << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *src << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -394,19 +394,19 @@ case 0x8b: { // copy r32 to r/m32
|
|||
05 00 00 00 02
|
||||
== 0x2000 # data segment
|
||||
08 00 00 00 # 8
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: ff
|
||||
+run: jump to r/m32
|
||||
+run: effective address is 0x00002000 (EAX)
|
||||
+run: jumping to 0x00000008
|
||||
+run: inst: 0x00000008
|
||||
-run: inst: 0x00000003
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: 0x00000003 opcode: 05
|
||||
|
||||
:(before "End Op ff Subops")
|
||||
case 4: { // jump to r/m32
|
||||
trace(90, "run") << "jump to r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "jump to r/m32" << end();
|
||||
const int32_t* arg2 = effective_address(modrm);
|
||||
EIP = *arg2;
|
||||
trace(90, "run") << "jumping to 0x" << HEXWORD << EIP << end();
|
||||
trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -428,7 +428,7 @@ af 00 00 00 # 0xaf
|
|||
|
||||
:(before "End Op ff Subops")
|
||||
case 6: { // push r/m32 to stack
|
||||
trace(90, "run") << "push r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "push r/m32" << end();
|
||||
const int32_t* val = effective_address(modrm);
|
||||
push(*val);
|
||||
break;
|
||||
|
@ -459,7 +459,7 @@ case 0x8f: { // pop stack into r/m32
|
|||
const uint8_t subop = (modrm>>3)&0x7;
|
||||
switch (subop) {
|
||||
case 0: {
|
||||
trace(90, "run") << "pop into r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "pop into r/m32" << end();
|
||||
int32_t* dest = effective_address(modrm);
|
||||
*dest = pop();
|
||||
break;
|
||||
|
@ -485,7 +485,7 @@ case 0x8f: { // pop stack into r/m32
|
|||
:(before "End Mod 0 Special-cases(addr)")
|
||||
case 5: // exception: mod 0b00 rm 0b101 => incoming disp32
|
||||
addr = next32();
|
||||
trace(90, "run") << "effective address is 0x" << HEXWORD << addr << " (disp32)" << end();
|
||||
trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (disp32)" << end();
|
||||
break;
|
||||
|
||||
//:
|
||||
|
@ -509,13 +509,13 @@ case 1: // indirect + disp8 addressing
|
|||
switch (rm) {
|
||||
default:
|
||||
addr = Reg[rm].u;
|
||||
trace(90, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
|
||||
trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
|
||||
break;
|
||||
// End Mod 1 Special-cases(addr)
|
||||
}
|
||||
if (addr > 0) {
|
||||
addr += static_cast<int8_t>(next());
|
||||
trace(90, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp8)" << end();
|
||||
trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp8)" << end();
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -554,13 +554,13 @@ case 2: // indirect + disp32 addressing
|
|||
switch (rm) {
|
||||
default:
|
||||
addr = Reg[rm].u;
|
||||
trace(90, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
|
||||
trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(rm) << ")" << end();
|
||||
break;
|
||||
// End Mod 2 Special-cases(addr)
|
||||
}
|
||||
if (addr > 0) {
|
||||
addr += next32();
|
||||
trace(90, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp32)" << end();
|
||||
trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding disp32)" << end();
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -596,7 +596,7 @@ put_new(Name, "8d", "copy address in rm32 into r32 (lea)");
|
|||
case 0x8d: { // copy address of m32 to r32
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t arg1 = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "copy address into " << rname(arg1) << end();
|
||||
trace(Callstack_depth+1, "run") << "copy address into " << rname(arg1) << end();
|
||||
Reg[arg1].u = effective_address_number(modrm);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -17,15 +17,15 @@ put_new(Name, "81", "combine rm32 with imm32 based on subop (add/sub/and/or/xor/
|
|||
|
||||
:(before "End Single-Byte Opcodes")
|
||||
case 0x81: { // combine imm32 with r/m32
|
||||
trace(90, "run") << "combine imm32 with r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "combine imm32 with r/m32" << end();
|
||||
const uint8_t modrm = next();
|
||||
int32_t* arg1 = effective_address(modrm);
|
||||
const int32_t arg2 = next32();
|
||||
trace(90, "run") << "imm32 is 0x" << HEXWORD << arg2 << end();
|
||||
trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << arg2 << end();
|
||||
const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits
|
||||
switch (subop) {
|
||||
case 0:
|
||||
trace(90, "run") << "subop add" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop add" << end();
|
||||
BINARY_ARITHMETIC_OP(+, *arg1, arg2);
|
||||
break;
|
||||
// End Op 81 Subops
|
||||
|
@ -68,7 +68,7 @@ put_new(Name, "2d", "subtract imm32 from EAX (sub)");
|
|||
:(before "End Single-Byte Opcodes")
|
||||
case 0x2d: { // subtract imm32 from EAX
|
||||
const int32_t arg2 = next32();
|
||||
trace(90, "run") << "subtract imm32 0x" << HEXWORD << arg2 << " from EAX" << end();
|
||||
trace(Callstack_depth+1, "run") << "subtract imm32 0x" << HEXWORD << arg2 << " from EAX" << end();
|
||||
BINARY_ARITHMETIC_OP(-, Reg[EAX].i, arg2);
|
||||
break;
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ case 0x2d: { // subtract imm32 from EAX
|
|||
|
||||
:(before "End Op 81 Subops")
|
||||
case 5: {
|
||||
trace(90, "run") << "subop subtract" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop subtract" << end();
|
||||
BINARY_ARITHMETIC_OP(-, *arg1, arg2);
|
||||
break;
|
||||
}
|
||||
|
@ -129,12 +129,12 @@ put_new(Name, "c1", "shift rm32 by imm8 bits depending on subop (sal/sar/shl/shr
|
|||
:(before "End Single-Byte Opcodes")
|
||||
case 0xc1: {
|
||||
const uint8_t modrm = next();
|
||||
trace(90, "run") << "operate on r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "operate on r/m32" << end();
|
||||
int32_t* arg1 = effective_address(modrm);
|
||||
const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits
|
||||
switch (subop) {
|
||||
case 4: { // shift left r/m32 by CL
|
||||
trace(90, "run") << "subop: shift left by CL bits" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop: shift left by CL bits" << end();
|
||||
uint8_t count = next() & 0x1f;
|
||||
// OF is only defined if count is 1
|
||||
if (count == 1) {
|
||||
|
@ -145,7 +145,7 @@ case 0xc1: {
|
|||
*arg1 = (*arg1 << count);
|
||||
ZF = (*arg1 == 0);
|
||||
SF = (*arg1 < 0);
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
break;
|
||||
}
|
||||
// End Op c1 Subops
|
||||
|
@ -171,14 +171,14 @@ case 0xc1: {
|
|||
|
||||
:(before "End Op c1 Subops")
|
||||
case 7: { // shift right r/m32 by CL, preserving sign
|
||||
trace(90, "run") << "subop: shift right by CL bits, while preserving sign" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while preserving sign" << end();
|
||||
uint8_t count = next() & 0x1f;
|
||||
*arg1 = (*arg1 >> count);
|
||||
ZF = (*arg1 == 0);
|
||||
SF = (*arg1 < 0);
|
||||
// OF is only defined if count is 1
|
||||
if (count == 1) OF = false;
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -221,7 +221,7 @@ case 7: { // shift right r/m32 by CL, preserving sign
|
|||
|
||||
:(before "End Op c1 Subops")
|
||||
case 5: { // shift right r/m32 by CL, preserving sign
|
||||
trace(90, "run") << "subop: shift right by CL bits, while padding zeroes" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop: shift right by CL bits, while padding zeroes" << end();
|
||||
uint8_t count = next() & 0x1f;
|
||||
// OF is only defined if count is 1
|
||||
if (count == 1) {
|
||||
|
@ -234,7 +234,7 @@ case 5: { // shift right r/m32 by CL, preserving sign
|
|||
ZF = (*uarg1 == 0);
|
||||
// result is always positive by definition
|
||||
SF = false;
|
||||
trace(90, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXWORD << *arg1 << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -277,7 +277,7 @@ put_new(Name, "25", "EAX = bitwise AND of imm32 with EAX (and)");
|
|||
:(before "End Single-Byte Opcodes")
|
||||
case 0x25: { // and imm32 with EAX
|
||||
const int32_t arg2 = next32();
|
||||
trace(90, "run") << "and imm32 0x" << HEXWORD << arg2 << " with EAX" << end();
|
||||
trace(Callstack_depth+1, "run") << "and imm32 0x" << HEXWORD << arg2 << " with EAX" << end();
|
||||
BINARY_BITWISE_OP(&, Reg[EAX].i, arg2);
|
||||
break;
|
||||
}
|
||||
|
@ -300,7 +300,7 @@ ff 00 00 00 # 0xff
|
|||
|
||||
:(before "End Op 81 Subops")
|
||||
case 4: {
|
||||
trace(90, "run") << "subop and" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop and" << end();
|
||||
BINARY_BITWISE_OP(&, *arg1, arg2);
|
||||
break;
|
||||
}
|
||||
|
@ -335,7 +335,7 @@ put_new(Name, "0d", "EAX = bitwise OR of imm32 with EAX (or)");
|
|||
:(before "End Single-Byte Opcodes")
|
||||
case 0x0d: { // or imm32 with EAX
|
||||
const int32_t arg2 = next32();
|
||||
trace(90, "run") << "or imm32 0x" << HEXWORD << arg2 << " with EAX" << end();
|
||||
trace(Callstack_depth+1, "run") << "or imm32 0x" << HEXWORD << arg2 << " with EAX" << end();
|
||||
BINARY_BITWISE_OP(|, Reg[EAX].i, arg2);
|
||||
break;
|
||||
}
|
||||
|
@ -358,7 +358,7 @@ a0 b0 c0 d0 # 0xd0c0b0a0
|
|||
|
||||
:(before "End Op 81 Subops")
|
||||
case 1: {
|
||||
trace(90, "run") << "subop or" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop or" << end();
|
||||
BINARY_BITWISE_OP(|, *arg1, arg2);
|
||||
break;
|
||||
}
|
||||
|
@ -391,7 +391,7 @@ put_new(Name, "35", "EAX = bitwise XOR of imm32 with EAX (xor)");
|
|||
:(before "End Single-Byte Opcodes")
|
||||
case 0x35: { // xor imm32 with EAX
|
||||
const int32_t arg2 = next32();
|
||||
trace(90, "run") << "xor imm32 0x" << HEXWORD << arg2 << " with EAX" << end();
|
||||
trace(Callstack_depth+1, "run") << "xor imm32 0x" << HEXWORD << arg2 << " with EAX" << end();
|
||||
BINARY_BITWISE_OP(^, Reg[EAX].i, arg2);
|
||||
break;
|
||||
}
|
||||
|
@ -414,7 +414,7 @@ a0 b0 c0 d0 # 0xd0c0b0a0
|
|||
|
||||
:(before "End Op 81 Subops")
|
||||
case 6: {
|
||||
trace(90, "run") << "subop xor" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop xor" << end();
|
||||
BINARY_BITWISE_OP(^, *arg1, arg2);
|
||||
break;
|
||||
}
|
||||
|
@ -448,13 +448,13 @@ put_new(Name, "3d", "compare: set SF if EAX < imm32 (cmp)");
|
|||
case 0x3d: { // compare EAX with imm32
|
||||
const int32_t arg1 = Reg[EAX].i;
|
||||
const int32_t arg2 = next32();
|
||||
trace(90, "run") << "compare EAX and imm32 0x" << HEXWORD << arg2 << end();
|
||||
trace(Callstack_depth+1, "run") << "compare EAX and imm32 0x" << HEXWORD << arg2 << end();
|
||||
const int32_t tmp1 = arg1 - arg2;
|
||||
SF = (tmp1 < 0);
|
||||
ZF = (tmp1 == 0);
|
||||
const int64_t tmp2 = arg1 - arg2;
|
||||
OF = (tmp1 != tmp2);
|
||||
trace(90, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
|
||||
trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -489,13 +489,13 @@ case 0x3d: { // compare EAX with imm32
|
|||
|
||||
:(before "End Op 81 Subops")
|
||||
case 7: {
|
||||
trace(90, "run") << "subop compare" << end();
|
||||
trace(Callstack_depth+1, "run") << "subop compare" << end();
|
||||
const int32_t tmp1 = *arg1 - arg2;
|
||||
SF = (tmp1 < 0);
|
||||
ZF = (tmp1 == 0);
|
||||
const int64_t tmp2 = *arg1 - arg2;
|
||||
OF = (tmp1 != tmp2);
|
||||
trace(90, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
|
||||
trace(Callstack_depth+1, "run") << "SF=" << SF << "; ZF=" << ZF << "; OF=" << OF << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -590,7 +590,7 @@ case 0xbe:
|
|||
case 0xbf: { // copy imm32 to r32
|
||||
const uint8_t rdest = op & 0x7;
|
||||
const int32_t src = next32();
|
||||
trace(90, "run") << "copy imm32 0x" << HEXWORD << src << " to " << rname(rdest) << end();
|
||||
trace(Callstack_depth+1, "run") << "copy imm32 0x" << HEXWORD << src << " to " << rname(rdest) << end();
|
||||
Reg[rdest].i = src;
|
||||
break;
|
||||
}
|
||||
|
@ -613,7 +613,7 @@ put_new(Name, "c7", "copy imm32 to rm32 (mov)");
|
|||
:(before "End Single-Byte Opcodes")
|
||||
case 0xc7: { // copy imm32 to r32
|
||||
const uint8_t modrm = next();
|
||||
trace(90, "run") << "copy imm32 to r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "copy imm32 to r/m32" << end();
|
||||
const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits
|
||||
if (subop != 0) {
|
||||
cerr << "unrecognized subop for opcode c7: " << NUM(subop) << " (only 0/copy currently implemented)\n";
|
||||
|
@ -621,7 +621,7 @@ case 0xc7: { // copy imm32 to r32
|
|||
}
|
||||
int32_t* dest = effective_address(modrm);
|
||||
const int32_t src = next32();
|
||||
trace(90, "run") << "imm32 is 0x" << HEXWORD << src << end();
|
||||
trace(Callstack_depth+1, "run") << "imm32 is 0x" << HEXWORD << src << end();
|
||||
*dest = src;
|
||||
break;
|
||||
}
|
||||
|
@ -643,10 +643,10 @@ put_new(Name, "68", "push imm32 to stack (push)");
|
|||
:(before "End Single-Byte Opcodes")
|
||||
case 0x68: {
|
||||
const uint32_t val = static_cast<uint32_t>(next32());
|
||||
trace(90, "run") << "push imm32 0x" << HEXWORD << val << end();
|
||||
trace(Callstack_depth+1, "run") << "push imm32 0x" << HEXWORD << val << end();
|
||||
//? cerr << "push: " << val << " => " << Reg[ESP].u << '\n';
|
||||
push(val);
|
||||
trace(90, "run") << "ESP is now 0x" << HEXWORD << Reg[ESP].u << end();
|
||||
trace(90, "run") << "contents at ESP: 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << end();
|
||||
trace(Callstack_depth+1, "run") << "ESP is now 0x" << HEXWORD << Reg[ESP].u << end();
|
||||
trace(Callstack_depth+1, "run") << "contents at ESP: 0x" << HEXWORD << read_mem_u32(Reg[ESP].u) << end();
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -26,22 +26,22 @@ uint32_t effective_address_from_sib(uint8_t mod) {
|
|||
uint32_t addr = 0;
|
||||
if (base != EBP || mod != 0) {
|
||||
addr = Reg[base].u;
|
||||
trace(90, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(base) << ")" << end();
|
||||
trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (" << rname(base) << ")" << end();
|
||||
}
|
||||
else {
|
||||
// base == EBP && mod == 0
|
||||
addr = next32(); // ignore base
|
||||
trace(90, "run") << "effective address is initially 0x" << HEXWORD << addr << " (disp32)" << end();
|
||||
trace(Callstack_depth+1, "run") << "effective address is initially 0x" << HEXWORD << addr << " (disp32)" << end();
|
||||
}
|
||||
const uint8_t index = (sib>>3)&0x7;
|
||||
if (index == ESP) {
|
||||
// ignore index and scale
|
||||
trace(90, "run") << "effective address is 0x" << HEXWORD << addr << end();
|
||||
trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << end();
|
||||
}
|
||||
else {
|
||||
const uint8_t scale = (1 << (sib>>6));
|
||||
addr += Reg[index].i*scale; // treat index register as signed. Maybe base as well? But we'll always ensure it's non-negative.
|
||||
trace(90, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding " << rname(index) << "*" << NUM(scale) << ")" << end();
|
||||
trace(Callstack_depth+1, "run") << "effective address is 0x" << HEXWORD << addr << " (after adding " << rname(index) << "*" << NUM(scale) << ")" << end();
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
|
|
@ -11,15 +11,15 @@ put_new(Name, "eb", "jump disp8 bytes away (jmp)");
|
|||
eb 05 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: eb
|
||||
+run: jump 5
|
||||
+run: inst: 0x00000008
|
||||
-run: inst: 0x00000003
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: 0x00000003 opcode: 05
|
||||
|
||||
:(before "End Single-Byte Opcodes")
|
||||
case 0xeb: { // jump rel8
|
||||
int8_t offset = static_cast<int>(next());
|
||||
trace(90, "run") << "jump " << NUM(offset) << end();
|
||||
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
|
||||
EIP += offset;
|
||||
break;
|
||||
}
|
||||
|
@ -36,16 +36,16 @@ put_new(Name, "74", "jump disp8 bytes away if equal, if ZF is set (jcc/jz/je)");
|
|||
74 05 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: 74
|
||||
+run: jump 5
|
||||
+run: inst: 0x00000008
|
||||
-run: inst: 0x00000003
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: 0x00000003 opcode: 05
|
||||
|
||||
:(before "End Single-Byte Opcodes")
|
||||
case 0x74: { // jump rel8 if ZF
|
||||
const int8_t offset = static_cast<int>(next());
|
||||
if (ZF) {
|
||||
trace(90, "run") << "jump " << NUM(offset) << end();
|
||||
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
|
||||
EIP += offset;
|
||||
}
|
||||
break;
|
||||
|
@ -58,9 +58,9 @@ case 0x74: { // jump rel8 if ZF
|
|||
74 05 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: inst: 0x00000003
|
||||
+run: inst: 0x00000008
|
||||
+run: 0x00000001 opcode: 74
|
||||
+run: 0x00000003 opcode: 05
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: jump 5
|
||||
|
||||
//:: jump if not equal/not zero
|
||||
|
@ -75,16 +75,16 @@ put_new(Name, "75", "jump disp8 bytes away if not equal, if ZF is not set (jcc/j
|
|||
75 05 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: 75
|
||||
+run: jump 5
|
||||
+run: inst: 0x00000008
|
||||
-run: inst: 0x00000003
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: 0x00000003 opcode: 05
|
||||
|
||||
:(before "End Single-Byte Opcodes")
|
||||
case 0x75: { // jump rel8 unless ZF
|
||||
const int8_t offset = static_cast<int>(next());
|
||||
if (!ZF) {
|
||||
trace(90, "run") << "jump " << NUM(offset) << end();
|
||||
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
|
||||
EIP += offset;
|
||||
}
|
||||
break;
|
||||
|
@ -97,9 +97,9 @@ case 0x75: { // jump rel8 unless ZF
|
|||
75 05 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: inst: 0x00000003
|
||||
+run: inst: 0x00000008
|
||||
+run: 0x00000001 opcode: 75
|
||||
+run: 0x00000003 opcode: 05
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: jump 5
|
||||
|
||||
//:: jump if greater
|
||||
|
@ -116,16 +116,16 @@ put_new(Name, "7f", "jump disp8 bytes away if greater, if ZF is unset and SF ==
|
|||
7f 05 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: 7f
|
||||
+run: jump 5
|
||||
+run: inst: 0x00000008
|
||||
-run: inst: 0x00000003
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: 0x00000003 opcode: 05
|
||||
|
||||
:(before "End Single-Byte Opcodes")
|
||||
case 0x7f: { // jump rel8 if !SF and !ZF
|
||||
const int8_t offset = static_cast<int>(next());
|
||||
if (!ZF && SF == OF) {
|
||||
trace(90, "run") << "jump " << NUM(offset) << end();
|
||||
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
|
||||
EIP += offset;
|
||||
}
|
||||
break;
|
||||
|
@ -140,9 +140,9 @@ case 0x7f: { // jump rel8 if !SF and !ZF
|
|||
7f 05 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: inst: 0x00000003
|
||||
+run: inst: 0x00000008
|
||||
+run: 0x00000001 opcode: 7f
|
||||
+run: 0x00000003 opcode: 05
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: jump 5
|
||||
|
||||
//:: jump if greater or equal
|
||||
|
@ -158,16 +158,16 @@ put_new(Name, "7d", "jump disp8 bytes away if greater or equal, if SF == OF (jcc
|
|||
7d 05 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: 7d
|
||||
+run: jump 5
|
||||
+run: inst: 0x00000008
|
||||
-run: inst: 0x00000003
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: 0x00000003 opcode: 05
|
||||
|
||||
:(before "End Single-Byte Opcodes")
|
||||
case 0x7d: { // jump rel8 if !SF
|
||||
const int8_t offset = static_cast<int>(next());
|
||||
if (SF == OF) {
|
||||
trace(90, "run") << "jump " << NUM(offset) << end();
|
||||
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
|
||||
EIP += offset;
|
||||
}
|
||||
break;
|
||||
|
@ -181,9 +181,9 @@ case 0x7d: { // jump rel8 if !SF
|
|||
7d 05 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: inst: 0x00000003
|
||||
+run: inst: 0x00000008
|
||||
+run: 0x00000001 opcode: 7d
|
||||
+run: 0x00000003 opcode: 05
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: jump 5
|
||||
|
||||
//:: jump if lesser
|
||||
|
@ -200,16 +200,16 @@ put_new(Name, "7c", "jump disp8 bytes away if lesser, if SF != OF (jcc/jl/jnge)"
|
|||
7c 05 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: 7c
|
||||
+run: jump 5
|
||||
+run: inst: 0x00000008
|
||||
-run: inst: 0x00000003
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: 0x00000003 opcode: 05
|
||||
|
||||
:(before "End Single-Byte Opcodes")
|
||||
case 0x7c: { // jump rel8 if SF and !ZF
|
||||
const int8_t offset = static_cast<int>(next());
|
||||
if (SF != OF) {
|
||||
trace(90, "run") << "jump " << NUM(offset) << end();
|
||||
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
|
||||
EIP += offset;
|
||||
}
|
||||
break;
|
||||
|
@ -224,9 +224,9 @@ case 0x7c: { // jump rel8 if SF and !ZF
|
|||
7c 05 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: inst: 0x00000003
|
||||
+run: inst: 0x00000008
|
||||
+run: 0x00000001 opcode: 7c
|
||||
+run: 0x00000003 opcode: 05
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: jump 5
|
||||
|
||||
//:: jump if lesser or equal
|
||||
|
@ -243,10 +243,10 @@ put_new(Name, "7e", "jump disp8 bytes away if lesser or equal, if ZF is set or S
|
|||
7e 05 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: 7e
|
||||
+run: jump 5
|
||||
+run: inst: 0x00000008
|
||||
-run: inst: 0x00000003
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: 0x00000003 opcode: 05
|
||||
|
||||
:(scenario jle_rel8_lesser)
|
||||
% ZF = false;
|
||||
|
@ -257,16 +257,16 @@ put_new(Name, "7e", "jump disp8 bytes away if lesser or equal, if ZF is set or S
|
|||
7e 05 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: 7e
|
||||
+run: jump 5
|
||||
+run: inst: 0x00000008
|
||||
-run: inst: 0x00000003
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: 0x00000003 opcode: 05
|
||||
|
||||
:(before "End Single-Byte Opcodes")
|
||||
case 0x7e: { // jump rel8 if SF or ZF
|
||||
const int8_t offset = static_cast<int>(next());
|
||||
if (ZF || SF != OF) {
|
||||
trace(90, "run") << "jump " << NUM(offset) << end();
|
||||
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
|
||||
EIP += offset;
|
||||
}
|
||||
break;
|
||||
|
@ -281,7 +281,7 @@ case 0x7e: { // jump rel8 if SF or ZF
|
|||
7e 05 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: inst: 0x00000003
|
||||
+run: inst: 0x00000008
|
||||
+run: 0x00000001 opcode: 7e
|
||||
+run: 0x00000003 opcode: 05
|
||||
+run: 0x00000008 opcode: 05
|
||||
-run: jump 5
|
||||
|
|
|
@ -11,15 +11,15 @@ put_new(Name, "e9", "jump disp32 bytes away (jmp)");
|
|||
e9 05 00 00 00 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: e9
|
||||
+run: jump 5
|
||||
+run: inst: 0x0000000b
|
||||
-run: inst: 0x00000006
|
||||
+run: 0x0000000b opcode: 05
|
||||
-run: 0x00000006 opcode: 05
|
||||
|
||||
:(before "End Single-Byte Opcodes")
|
||||
case 0xe9: { // jump disp32
|
||||
const int32_t offset = next32();
|
||||
trace(90, "run") << "jump " << offset << end();
|
||||
trace(Callstack_depth+1, "run") << "jump " << offset << end();
|
||||
EIP += offset;
|
||||
break;
|
||||
}
|
||||
|
@ -36,16 +36,16 @@ put_new(Name_0f, "84", "jump disp32 bytes away if equal, if ZF is set (jcc/jz/je
|
|||
0f 84 05 00 00 00 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: 0f
|
||||
+run: jump 5
|
||||
+run: inst: 0x0000000c
|
||||
-run: inst: 0x00000007
|
||||
+run: 0x0000000c opcode: 05
|
||||
-run: 0x00000007 opcode: 05
|
||||
|
||||
:(before "End Two-Byte Opcodes Starting With 0f")
|
||||
case 0x84: { // jump disp32 if ZF
|
||||
const int32_t offset = next32();
|
||||
if (ZF) {
|
||||
trace(90, "run") << "jump " << NUM(offset) << end();
|
||||
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
|
||||
EIP += offset;
|
||||
}
|
||||
break;
|
||||
|
@ -58,9 +58,9 @@ case 0x84: { // jump disp32 if ZF
|
|||
0f 84 05 00 00 00 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: inst: 0x00000007
|
||||
+run: inst: 0x0000000c
|
||||
+run: 0x00000001 opcode: 0f
|
||||
+run: 0x00000007 opcode: 05
|
||||
+run: 0x0000000c opcode: 05
|
||||
-run: jump 5
|
||||
|
||||
//:: jump if not equal/not zero
|
||||
|
@ -75,16 +75,16 @@ put_new(Name_0f, "85", "jump disp32 bytes away if not equal, if ZF is not set (j
|
|||
0f 85 05 00 00 00 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: 0f
|
||||
+run: jump 5
|
||||
+run: inst: 0x0000000c
|
||||
-run: inst: 0x00000007
|
||||
+run: 0x0000000c opcode: 05
|
||||
-run: 0x00000007 opcode: 05
|
||||
|
||||
:(before "End Two-Byte Opcodes Starting With 0f")
|
||||
case 0x85: { // jump disp32 unless ZF
|
||||
const int32_t offset = next32();
|
||||
if (!ZF) {
|
||||
trace(90, "run") << "jump " << NUM(offset) << end();
|
||||
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
|
||||
EIP += offset;
|
||||
}
|
||||
break;
|
||||
|
@ -97,9 +97,9 @@ case 0x85: { // jump disp32 unless ZF
|
|||
0f 85 05 00 00 00 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: inst: 0x00000007
|
||||
+run: inst: 0x0000000c
|
||||
+run: 0x00000001 opcode: 0f
|
||||
+run: 0x00000007 opcode: 05
|
||||
+run: 0x0000000c opcode: 05
|
||||
-run: jump 5
|
||||
|
||||
//:: jump if greater
|
||||
|
@ -116,16 +116,16 @@ put_new(Name_0f, "8f", "jump disp32 bytes away if greater, if ZF is unset and SF
|
|||
0f 8f 05 00 00 00 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: 0f
|
||||
+run: jump 5
|
||||
+run: inst: 0x0000000c
|
||||
-run: inst: 0x00000007
|
||||
+run: 0x0000000c opcode: 05
|
||||
-run: 0x00000007 opcode: 05
|
||||
|
||||
:(before "End Two-Byte Opcodes Starting With 0f")
|
||||
case 0x8f: { // jump disp32 if !SF and !ZF
|
||||
const int32_t offset = next32();
|
||||
if (!ZF && SF == OF) {
|
||||
trace(90, "run") << "jump " << NUM(offset) << end();
|
||||
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
|
||||
EIP += offset;
|
||||
}
|
||||
break;
|
||||
|
@ -140,9 +140,9 @@ case 0x8f: { // jump disp32 if !SF and !ZF
|
|||
0f 8f 05 00 00 00 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: inst: 0x00000007
|
||||
+run: inst: 0x0000000c
|
||||
+run: 0x00000001 opcode: 0f
|
||||
+run: 0x00000007 opcode: 05
|
||||
+run: 0x0000000c opcode: 05
|
||||
-run: jump 5
|
||||
|
||||
//:: jump if greater or equal
|
||||
|
@ -158,16 +158,16 @@ put_new(Name_0f, "8d", "jump disp32 bytes away if greater or equal, if SF == OF
|
|||
0f 8d 05 00 00 00 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: 0f
|
||||
+run: jump 5
|
||||
+run: inst: 0x0000000c
|
||||
-run: inst: 0x00000007
|
||||
+run: 0x0000000c opcode: 05
|
||||
-run: 0x00000007 opcode: 05
|
||||
|
||||
:(before "End Two-Byte Opcodes Starting With 0f")
|
||||
case 0x8d: { // jump disp32 if !SF
|
||||
const int32_t offset = next32();
|
||||
if (SF == OF) {
|
||||
trace(90, "run") << "jump " << NUM(offset) << end();
|
||||
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
|
||||
EIP += offset;
|
||||
}
|
||||
break;
|
||||
|
@ -181,9 +181,9 @@ case 0x8d: { // jump disp32 if !SF
|
|||
0f 8d 05 00 00 00 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: inst: 0x00000007
|
||||
+run: inst: 0x0000000c
|
||||
+run: 0x00000001 opcode: 0f
|
||||
+run: 0x00000007 opcode: 05
|
||||
+run: 0x0000000c opcode: 05
|
||||
-run: jump 5
|
||||
|
||||
//:: jump if lesser
|
||||
|
@ -200,16 +200,16 @@ put_new(Name_0f, "8c", "jump disp32 bytes away if lesser, if SF != OF (jcc/jl/jn
|
|||
0f 8c 05 00 00 00 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: 0f
|
||||
+run: jump 5
|
||||
+run: inst: 0x0000000c
|
||||
-run: inst: 0x00000007
|
||||
+run: 0x0000000c opcode: 05
|
||||
-run: 0x00000007 opcode: 05
|
||||
|
||||
:(before "End Two-Byte Opcodes Starting With 0f")
|
||||
case 0x8c: { // jump disp32 if SF and !ZF
|
||||
const int32_t offset = next32();
|
||||
if (SF != OF) {
|
||||
trace(90, "run") << "jump " << NUM(offset) << end();
|
||||
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
|
||||
EIP += offset;
|
||||
}
|
||||
break;
|
||||
|
@ -224,9 +224,9 @@ case 0x8c: { // jump disp32 if SF and !ZF
|
|||
0f 8c 05 00 00 00 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: inst: 0x00000007
|
||||
+run: inst: 0x0000000c
|
||||
+run: 0x00000001 opcode: 0f
|
||||
+run: 0x00000007 opcode: 05
|
||||
+run: 0x0000000c opcode: 05
|
||||
-run: jump 5
|
||||
|
||||
//:: jump if lesser or equal
|
||||
|
@ -243,10 +243,10 @@ put_new(Name_0f, "8e", "jump disp32 bytes away if lesser or equal, if ZF is set
|
|||
0f 8e 05 00 00 00 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: 0f
|
||||
+run: jump 5
|
||||
+run: inst: 0x0000000c
|
||||
-run: inst: 0x00000007
|
||||
+run: 0x0000000c opcode: 05
|
||||
-run: 0x00000007 opcode: 05
|
||||
|
||||
:(scenario jle_disp32_lesser)
|
||||
% ZF = false;
|
||||
|
@ -257,16 +257,16 @@ put_new(Name_0f, "8e", "jump disp32 bytes away if lesser or equal, if ZF is set
|
|||
0f 8e 05 00 00 00 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: 0x00000001 opcode: 0f
|
||||
+run: jump 5
|
||||
+run: inst: 0x0000000c
|
||||
-run: inst: 0x00000007
|
||||
+run: 0x0000000c opcode: 05
|
||||
-run: 0x00000007 opcode: 05
|
||||
|
||||
:(before "End Two-Byte Opcodes Starting With 0f")
|
||||
case 0x8e: { // jump disp32 if SF or ZF
|
||||
const int32_t offset = next32();
|
||||
if (ZF || SF != OF) {
|
||||
trace(90, "run") << "jump " << NUM(offset) << end();
|
||||
trace(Callstack_depth+1, "run") << "jump " << NUM(offset) << end();
|
||||
EIP += offset;
|
||||
}
|
||||
break;
|
||||
|
@ -281,7 +281,7 @@ case 0x8e: { // jump disp32 if SF or ZF
|
|||
0f 8e 05 00 00 00 # skip 1 instruction
|
||||
05 00 00 00 01
|
||||
05 00 00 00 02
|
||||
+run: inst: 0x00000001
|
||||
+run: inst: 0x00000007
|
||||
+run: inst: 0x0000000c
|
||||
+run: 0x00000001 opcode: 0f
|
||||
+run: 0x00000007 opcode: 05
|
||||
+run: 0x0000000c opcode: 05
|
||||
-run: jump 5
|
||||
|
|
|
@ -17,11 +17,12 @@ put_new(Name, "e8", "call disp32 (call)");
|
|||
:(before "End Single-Byte Opcodes")
|
||||
case 0xe8: { // call disp32 relative to next EIP
|
||||
const int32_t offset = next32();
|
||||
trace(90, "run") << "call imm32 0x" << HEXWORD << offset << end();
|
||||
++Callstack_depth;
|
||||
trace(Callstack_depth+1, "run") << "call imm32 0x" << HEXWORD << offset << end();
|
||||
//? cerr << "push: EIP: " << EIP << " => " << Reg[ESP].u << '\n';
|
||||
push(EIP);
|
||||
EIP += offset;
|
||||
trace(90, "run") << "jumping to 0x" << HEXWORD << EIP << end();
|
||||
trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -42,11 +43,12 @@ case 0xe8: { // call disp32 relative to next EIP
|
|||
|
||||
:(before "End Op ff Subops")
|
||||
case 2: { // call function pointer at r/m32
|
||||
trace(90, "run") << "call to r/m32" << end();
|
||||
trace(Callstack_depth+1, "run") << "call to r/m32" << end();
|
||||
const int32_t* offset = effective_address(modrm);
|
||||
push(EIP);
|
||||
EIP += *offset;
|
||||
trace(90, "run") << "jumping to 0x" << HEXWORD << EIP << end();
|
||||
trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
|
||||
++Callstack_depth;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -83,8 +85,9 @@ put_new(Name, "c3", "return from most recent unfinished call (ret)");
|
|||
|
||||
:(before "End Single-Byte Opcodes")
|
||||
case 0xc3: { // return from a call
|
||||
trace(90, "run") << "return" << end();
|
||||
trace(Callstack_depth+1, "run") << "return" << end();
|
||||
--Callstack_depth;
|
||||
EIP = pop();
|
||||
trace(90, "run") << "jumping to 0x" << HEXWORD << EIP << end();
|
||||
trace(Callstack_depth+1, "run") << "jumping to 0x" << HEXWORD << EIP << end();
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ put_new(Name, "cd", "software interrupt (int)");
|
|||
|
||||
:(before "End Single-Byte Opcodes")
|
||||
case 0xcd: { // int imm8 (software interrupt)
|
||||
trace(90, "run") << "syscall" << end();
|
||||
trace(Callstack_depth+1, "run") << "syscall" << end();
|
||||
uint8_t code = next();
|
||||
if (code != 0x80) {
|
||||
raise << "Unimplemented interrupt code " << HEXBYTE << code << '\n' << end();
|
||||
|
@ -21,67 +21,67 @@ void process_int80() {
|
|||
exit(/*exit code*/Reg[EBX].u);
|
||||
break;
|
||||
case 3:
|
||||
trace(91, "run") << "read: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end();
|
||||
trace(Callstack_depth+1, "run") << "read: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end();
|
||||
Reg[EAX].i = read(/*file descriptor*/Reg[EBX].u, /*memory buffer*/mem_addr_u8(Reg[ECX].u), /*size*/Reg[EDX].u);
|
||||
trace(91, "run") << "result: " << Reg[EAX].i << end();
|
||||
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
|
||||
if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
|
||||
break;
|
||||
case 4:
|
||||
trace(91, "run") << "write: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end();
|
||||
trace(91, "run") << Reg[ECX].u << " => " << mem_addr_string(Reg[ECX].u, Reg[EDX].u) << end();
|
||||
trace(Callstack_depth+1, "run") << "write: " << Reg[EBX].u << ' ' << Reg[ECX].u << ' ' << Reg[EDX].u << end();
|
||||
trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_string(Reg[ECX].u, Reg[EDX].u) << end();
|
||||
Reg[EAX].i = write(/*file descriptor*/Reg[EBX].u, /*memory buffer*/mem_addr_u8(Reg[ECX].u), /*size*/Reg[EDX].u);
|
||||
trace(91, "run") << "result: " << Reg[EAX].i << end();
|
||||
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
|
||||
if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
|
||||
break;
|
||||
case 5: {
|
||||
check_flags(ECX);
|
||||
check_mode(EDX);
|
||||
trace(91, "run") << "open: " << Reg[EBX].u << ' ' << Reg[ECX].u << end();
|
||||
trace(91, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
|
||||
trace(Callstack_depth+1, "run") << "open: " << Reg[EBX].u << ' ' << Reg[ECX].u << end();
|
||||
trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
|
||||
Reg[EAX].i = open(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*flags*/Reg[ECX].u, /*mode*/0640);
|
||||
trace(91, "run") << "result: " << Reg[EAX].i << end();
|
||||
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
|
||||
if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
|
||||
break;
|
||||
}
|
||||
case 6:
|
||||
trace(91, "run") << "close: " << Reg[EBX].u << end();
|
||||
trace(Callstack_depth+1, "run") << "close: " << Reg[EBX].u << end();
|
||||
Reg[EAX].i = close(/*file descriptor*/Reg[EBX].u);
|
||||
trace(91, "run") << "result: " << Reg[EAX].i << end();
|
||||
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
|
||||
if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
|
||||
break;
|
||||
case 8:
|
||||
check_mode(ECX);
|
||||
trace(91, "run") << "creat: " << Reg[EBX].u << end();
|
||||
trace(91, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
|
||||
trace(Callstack_depth+1, "run") << "creat: " << Reg[EBX].u << end();
|
||||
trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
|
||||
Reg[EAX].i = creat(/*filename*/mem_addr_kernel_string(Reg[EBX].u), /*mode*/0640);
|
||||
trace(91, "run") << "result: " << Reg[EAX].i << end();
|
||||
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
|
||||
if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
|
||||
break;
|
||||
case 10:
|
||||
trace(91, "run") << "unlink: " << Reg[EBX].u << end();
|
||||
trace(91, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
|
||||
trace(Callstack_depth+1, "run") << "unlink: " << Reg[EBX].u << end();
|
||||
trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
|
||||
Reg[EAX].i = unlink(/*filename*/mem_addr_kernel_string(Reg[EBX].u));
|
||||
trace(91, "run") << "result: " << Reg[EAX].i << end();
|
||||
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
|
||||
if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
|
||||
break;
|
||||
case 38:
|
||||
trace(91, "run") << "rename: " << Reg[EBX].u << " -> " << Reg[ECX].u << end();
|
||||
trace(91, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
|
||||
trace(91, "run") << Reg[ECX].u << " => " << mem_addr_kernel_string(Reg[ECX].u) << end();
|
||||
trace(Callstack_depth+1, "run") << "rename: " << Reg[EBX].u << " -> " << Reg[ECX].u << end();
|
||||
trace(Callstack_depth+1, "run") << Reg[EBX].u << " => " << mem_addr_kernel_string(Reg[EBX].u) << end();
|
||||
trace(Callstack_depth+1, "run") << Reg[ECX].u << " => " << mem_addr_kernel_string(Reg[ECX].u) << end();
|
||||
Reg[EAX].i = rename(/*old filename*/mem_addr_kernel_string(Reg[EBX].u), /*new filename*/mem_addr_kernel_string(Reg[ECX].u));
|
||||
trace(91, "run") << "result: " << Reg[EAX].i << end();
|
||||
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].i << end();
|
||||
if (Reg[EAX].i == -1) raise << strerror(errno) << '\n' << end();
|
||||
break;
|
||||
case 45: // brk: modify size of data segment
|
||||
trace(91, "run") << "grow data segment to " << Reg[EBX].u << end();
|
||||
trace(Callstack_depth+1, "run") << "grow data segment to " << Reg[EBX].u << end();
|
||||
grow_data_segment(/*new end address*/Reg[EBX].u);
|
||||
break;
|
||||
case 90: // mmap: allocate memory outside existing segment allocations
|
||||
trace(91, "run") << "mmap: allocate new segment" << end();
|
||||
trace(Callstack_depth+1, "run") << "mmap: allocate new segment" << end();
|
||||
// Ignore most arguments for now: address hint, protection flags, sharing flags, fd, offset.
|
||||
// We only support anonymous maps.
|
||||
Reg[EAX].u = new_segment(/*length*/read_mem_u32(Reg[EBX].u+0x4));
|
||||
trace(91, "run") << "result: " << Reg[EAX].u << end();
|
||||
trace(Callstack_depth+1, "run") << "result: " << Reg[EAX].u << end();
|
||||
break;
|
||||
default:
|
||||
raise << HEXWORD << EIP << ": unimplemented syscall " << Reg[EAX].u << '\n' << end();
|
||||
|
|
|
@ -23,7 +23,7 @@ uint8_t* effective_byte_address(uint8_t modrm) {
|
|||
uint8_t rm = modrm & 0x7;
|
||||
if (mod == 3) {
|
||||
// select an 8-bit register
|
||||
trace(90, "run") << "r/m8 is " << rname_8bit(rm) << end();
|
||||
trace(Callstack_depth+1, "run") << "r/m8 is " << rname_8bit(rm) << end();
|
||||
return reg_8bit(rm);
|
||||
}
|
||||
// the rest is as usual
|
||||
|
@ -58,12 +58,12 @@ f0 cc bb aa
|
|||
case 0x88: { // copy r8 to r/m8
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t rsrc = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "copy " << rname_8bit(rsrc) << " to r8/m8-at-r32" << end();
|
||||
trace(Callstack_depth+1, "run") << "copy " << rname_8bit(rsrc) << " to r8/m8-at-r32" << end();
|
||||
// use unsigned to zero-extend 8-bit value to 32 bits
|
||||
uint8_t* dest = reinterpret_cast<uint8_t*>(effective_byte_address(modrm));
|
||||
const uint8_t* src = reg_8bit(rsrc);
|
||||
*dest = *src;
|
||||
trace(90, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -91,14 +91,14 @@ ab ff ff ff # 0xab with more data in following bytes
|
|||
case 0x8a: { // copy r/m8 to r8
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t rdest = (modrm>>3)&0x7;
|
||||
trace(90, "run") << "copy r8/m8-at-r32 to " << rname_8bit(rdest) << end();
|
||||
trace(Callstack_depth+1, "run") << "copy r8/m8-at-r32 to " << rname_8bit(rdest) << end();
|
||||
// use unsigned to zero-extend 8-bit value to 32 bits
|
||||
const uint8_t* src = reinterpret_cast<uint8_t*>(effective_byte_address(modrm));
|
||||
uint8_t* dest = reg_8bit(rdest);
|
||||
trace(90, "run") << "storing 0x" << HEXBYTE << NUM(*src) << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*src) << end();
|
||||
*dest = *src;
|
||||
const uint8_t rdest_32bit = rdest & 0x3;
|
||||
trace(90, "run") << rname(rdest_32bit) << " now contains 0x" << HEXWORD << Reg[rdest_32bit].u << end();
|
||||
trace(Callstack_depth+1, "run") << rname(rdest_32bit) << " now contains 0x" << HEXWORD << Reg[rdest_32bit].u << end();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -137,8 +137,8 @@ f0 cc bb aa
|
|||
case 0xc6: { // copy imm8 to r/m8
|
||||
const uint8_t modrm = next();
|
||||
const uint8_t src = next();
|
||||
trace(90, "run") << "copy imm8 to r8/m8-at-r32" << end();
|
||||
trace(90, "run") << "imm8 is 0x" << HEXWORD << src << end();
|
||||
trace(Callstack_depth+1, "run") << "copy imm8 to r8/m8-at-r32" << end();
|
||||
trace(Callstack_depth+1, "run") << "imm8 is 0x" << HEXWORD << src << end();
|
||||
const uint8_t subop = (modrm>>3)&0x7; // middle 3 'reg opcode' bits
|
||||
if (subop != 0) {
|
||||
cerr << "unrecognized subop for opcode c6: " << NUM(subop) << " (only 0/copy currently implemented)\n";
|
||||
|
@ -147,6 +147,6 @@ case 0xc6: { // copy imm8 to r/m8
|
|||
// use unsigned to zero-extend 8-bit value to 32 bits
|
||||
uint8_t* dest = reinterpret_cast<uint8_t*>(effective_byte_address(modrm));
|
||||
*dest = src;
|
||||
trace(90, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
|
||||
trace(Callstack_depth+1, "run") << "storing 0x" << HEXBYTE << NUM(*dest) << end();
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ if (is_equal(argv[1], "translate")) {
|
|||
output_filename = argv[i];
|
||||
}
|
||||
else {
|
||||
trace(2, "parse") << argv[i] << end();
|
||||
ifstream fin(argv[i]);
|
||||
if (!fin) {
|
||||
cerr << "could not open " << argv[i] << '\n';
|
||||
|
@ -53,8 +54,10 @@ if (is_equal(argv[1], "translate")) {
|
|||
cerr << "must provide a filename to write to using '-o'\n";
|
||||
exit(1);
|
||||
}
|
||||
trace(2, "transform") << "begin" << end();
|
||||
transform(p);
|
||||
if (trace_contains_errors()) return 1;
|
||||
trace(2, "translate") << "begin" << end();
|
||||
save_elf(p, output_filename);
|
||||
if (trace_contains_errors()) {
|
||||
unlink(output_filename.c_str());
|
||||
|
|
|
@ -156,7 +156,7 @@ void pack_operands(program& p) {
|
|||
if (p.segments.empty()) return;
|
||||
segment& code = p.segments.at(0);
|
||||
// Pack Operands(segment code)
|
||||
trace(99, "transform") << "-- pack operands" << end();
|
||||
trace(3, "transform") << "-- pack operands" << end();
|
||||
for (int i = 0; i < SIZE(code.lines); ++i) {
|
||||
line& inst = code.lines.at(i);
|
||||
if (all_hex_bytes(inst)) continue;
|
||||
|
|
|
@ -13,7 +13,7 @@ if (trace_contains_errors()) return;
|
|||
|
||||
:(code)
|
||||
void check_operands(const segment& code) {
|
||||
trace(99, "transform") << "-- check operands" << end();
|
||||
trace(3, "transform") << "-- check operands" << end();
|
||||
for (int i = 0; i < SIZE(code.lines); ++i) {
|
||||
check_operands(code.lines.at(i));
|
||||
if (trace_contains_errors()) return; // stop at the first mal-formed instruction
|
||||
|
|
|
@ -27,7 +27,7 @@ check_operand_bounds(code);
|
|||
if (trace_contains_errors()) return;
|
||||
:(code)
|
||||
void check_operand_bounds(const segment& code) {
|
||||
trace(99, "transform") << "-- check operand bounds" << end();
|
||||
trace(3, "transform") << "-- check operand bounds" << end();
|
||||
for (int i = 0; i < SIZE(code.lines); ++i) {
|
||||
const line& inst = code.lines.at(i);
|
||||
for (int j = first_operand(inst); j < SIZE(inst.words); ++j)
|
||||
|
|
|
@ -31,7 +31,7 @@ Currently_parsing_segment_index = -1;
|
|||
if (!starts_with(segment_title, "0x")) {
|
||||
Currently_parsing_named_segment = true;
|
||||
if (!contains_key(Segment_index, segment_title)) {
|
||||
trace(99, "parse") << "new segment '" << segment_title << "'" << end();
|
||||
trace(3, "parse") << "new segment '" << segment_title << "'" << end();
|
||||
if (out.segments.empty() && segment_title != "code") {
|
||||
raise << "first segment must be 'code' but is '" << segment_title << "'\n" << end();
|
||||
return;
|
||||
|
@ -44,7 +44,7 @@ if (!starts_with(segment_title, "0x")) {
|
|||
out.segments.push_back(segment());
|
||||
}
|
||||
else {
|
||||
trace(99, "parse") << "appending to segment '" << segment_title << "'" << end();
|
||||
trace(3, "parse") << "appending to segment '" << segment_title << "'" << end();
|
||||
}
|
||||
Currently_parsing_segment_index = get(Segment_index, segment_title);
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ if (!starts_with(segment_title, "0x")) {
|
|||
:(before "End flush(p, lines) Special-cases")
|
||||
if (Currently_parsing_named_segment) {
|
||||
assert(!p.segments.empty());
|
||||
trace(99, "parse") << "flushing segment" << end();
|
||||
trace(3, "parse") << "flushing segment" << end();
|
||||
vector<line>& curr_segment_data = p.segments.at(Currently_parsing_segment_index).lines;
|
||||
curr_segment_data.insert(curr_segment_data.end(), lines.begin(), lines.end());
|
||||
lines.clear();
|
||||
|
@ -107,7 +107,7 @@ Transform.push_back(compute_segment_starts);
|
|||
|
||||
:(code)
|
||||
void compute_segment_starts(program& p) {
|
||||
trace(99, "transform") << "-- compute segment addresses" << end();
|
||||
trace(3, "transform") << "-- compute segment addresses" << end();
|
||||
uint32_t p_offset = /*size of ehdr*/0x34 + SIZE(p.segments)*0x20/*size of each phdr*/;
|
||||
for (size_t i = 0; i < p.segments.size(); ++i) {
|
||||
segment& curr = p.segments.at(i);
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
05 0x0d0c0b0a/imm32
|
||||
Entry:
|
||||
05 0x0d0c0b0a/imm32
|
||||
+run: inst: 0x00000006
|
||||
-run: inst: 0x00000001
|
||||
+run: 0x00000006 opcode: 05
|
||||
-run: 0x00000001 opcode: 05
|
||||
|
||||
:(before "End Globals")
|
||||
uint32_t Entry_address = 0;
|
||||
|
@ -97,7 +97,7 @@ loop:
|
|||
Transform.push_back(rewrite_labels);
|
||||
:(code)
|
||||
void rewrite_labels(program& p) {
|
||||
trace(99, "transform") << "-- rewrite labels" << end();
|
||||
trace(3, "transform") << "-- rewrite labels" << end();
|
||||
if (p.segments.empty()) return;
|
||||
segment& code = p.segments.at(0);
|
||||
map<string, int32_t> byte_index; // values are unsigned, but we're going to do subtractions on them so they need to fit in 31 bits
|
||||
|
|
|
@ -18,7 +18,7 @@ x:
|
|||
Transform.push_back(rewrite_global_variables);
|
||||
:(code)
|
||||
void rewrite_global_variables(program& p) {
|
||||
trace(99, "transform") << "-- rewrite global variables" << end();
|
||||
trace(3, "transform") << "-- rewrite global variables" << end();
|
||||
// Begin rewrite_global_variables
|
||||
map<string, uint32_t> address;
|
||||
compute_addresses_for_global_variables(p, address);
|
||||
|
|
|
@ -23,7 +23,7 @@ Transform.push_back(transform_literal_strings);
|
|||
int Next_auto_global = 1;
|
||||
:(code)
|
||||
void transform_literal_strings(program& p) {
|
||||
trace(99, "transform") << "-- move literal strings to data segment" << end();
|
||||
trace(3, "transform") << "-- move literal strings to data segment" << end();
|
||||
if (p.segments.empty()) return;
|
||||
segment& code = p.segments.at(0);
|
||||
segment data;
|
||||
|
|
|
@ -22,7 +22,7 @@ void load_map(const string& map_filename) {
|
|||
|
||||
:(after "Run One Instruction")
|
||||
if (contains_key(Symbol_name, EIP))
|
||||
trace(90, "run") << "== label " << get(Symbol_name, EIP) << end();
|
||||
trace(Callstack_depth, "run") << "== label " << get(Symbol_name, EIP) << end();
|
||||
|
||||
//: If a label starts with '$watch-', make a note of the effective address
|
||||
//: computed by the next instruction. Start dumping out its contents to the
|
||||
|
|
|
@ -26,13 +26,13 @@ test-foo: # offset 7
|
|||
c3/return
|
||||
|
||||
# check that code in test-foo ran (implicitly called by run-tests)
|
||||
+run: inst: 0x00000007
|
||||
+run: 0x00000007 opcode: 01
|
||||
|
||||
:(code)
|
||||
void create_test_function(program& p) {
|
||||
if (p.segments.empty()) return;
|
||||
segment& code = p.segments.at(0);
|
||||
trace(99, "transform") << "-- create 'run-tests'" << end();
|
||||
trace(3, "transform") << "-- create 'run-tests'" << end();
|
||||
vector<line> new_insts;
|
||||
for (int i = 0; i < SIZE(code.lines); ++i) {
|
||||
line& inst = code.lines.at(i);
|
||||
|
|
Loading…
Reference in New Issue