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:
Kartik Agaram 2019-02-25 00:17:46 -08:00
parent e5998f74ac
commit c442a5ad80
73 changed files with 1332 additions and 1004 deletions

View File

@ -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.

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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)

View File

@ -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")))

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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));
}

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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));

View File

@ -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();
}
}

View File

@ -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) {

View File

@ -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);

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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();
}
}
}

View File

@ -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();

View File

@ -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;

View File

@ -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)) {

View File

@ -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;
}

View File

@ -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());
}
}

View File

@ -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) {

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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() {

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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

78
browse_trace/Readme.md Normal file
View File

@ -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

7
browse_trace/browse_trace Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
set -e
cd `dirname $0`
./build
cd -
`dirname $0`/browse_trace_bin $*

View File

@ -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;
}
}

83
browse_trace/build Executable file
View File

@ -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

6
browse_trace/clean Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
set -e
set -v
rm -rf browse_trace_bin* *_list
rm -rf termbox/*.o termbox/libtermbox.a

View File

@ -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))

View File

@ -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')

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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());

View File

@ -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;

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -12,9 +12,9 @@ fi
if [[ $1 == 'ex'* ]]
then
CFLAGS=-g ./subx --map --trace run examples/$* 2>x
CFLAGS=-g ./subx --map --trace run examples/$*
exit $?
fi
CFLAGS=-g ./subx --map --trace run apps/$* 2>x
CFLAGS=-g ./subx --map --trace run apps/$*
exit $?