inline position positionFromLambda(lambda *func) {
if (func == 0)
return nullPos;
program& code = *func->code;
// Check for empty program.
if (code.begin() == code.end())
return nullPos;
return code.begin()->pos;
}
inline void printNameFromLambda(ostream& out, lambda *func) {
if (!func) {
out << "<top level>";
return;
}
#ifdef DEBUG_FRAME
string name = func->name;
#else
string name = "";
#endif
// If unnamed, use the pointer address.
if (name.empty())
out << func;
else
out << name;
out << " at ";
positionFromLambda(func).printTerse(out);
}
inline void printNameFromBltin(ostream& out, bltin b) {
#ifdef DEBUG_BLTIN
string name = lookupBltin(b);
#else
string name = "";
#endif
if (!name.empty())
out << name << " ";
out << "(builtin at " << (void *)b << ")";
}
class profiler : public gc {
// To do call graph analysis, each call stack that occurs in practice is
// represented by a node. For instance, if f and g are functions, then
// f -> g -> g
// is represented by a node and
// g -> f -> g
// is represented by a different one.
struct node {
// The top-level function of the call stack. It is either an asymptote
// function with a given lambda, or a builtin function, with a given
// bltin.
lambda *func;
bltin cfunc;
// The number of times the top-level function has been called resulting in
// this specific call stack.
int calls;
// The number of bytecode instructions executed with this exact call stack.
// It does not include time spent in called function.
int instructions;
// Number of instructions spent in this function or its children. This is
// computed by computeTotals.
int instTotal;
// The number of real-time nanoseconds spent in this node. WARNING: May
// be wildly inaccurate.
long long nsecs;
// Total including children.
long long nsecsTotal;
// Call stacks resulting from calls during this call stack.
mem::vector<node> children;
// Return the call stack resulting from a call to func when this call
// stack is current.
node *getChild(lambda *func) {
size_t n = children.size();
for (size_t i = 0; i < n; ++i)
if (children[i].func == func)
return &children[i];
// Not found, create a new one.
children.push_back(node(func));
return &children.back();
}
node *getChild(bltin func) {
size_t n = children.size();
for (size_t i = 0; i < n; ++i)
if (children[i].cfunc == func)
return &children[i];
// Not found, create a new one.
children.push_back(node(func));
return &children.back();
}
void computeTotals() {
instTotal = instructions;
nsecsTotal = nsecs;
size_t n = children.size();
for (size_t i = 0; i < n; ++i) {
children[i].computeTotals();
instTotal += children[i].instTotal;
nsecsTotal += children[i].nsecsTotal;
}
}
size_t n = children.size();
for (size_t i = 0; i < n; ++i) {
children[i].pydump(out);
out << ",\n";
}
out << " ])\n";
}
};
// An empty call stack.
node emptynode;
// All of the callstacks.
std::stack<node *> callstack;
node &topnode() {
return *callstack.top();
}
// Arc representing one function calling another. Used only for building
// the output for kcachegrind.
struct arc : public gc {
int calls;
int instTotal;
long long nsecsTotal;
// Representing one function and its calls to other functions.
struct fun : public gc {
int instructions;
long long nsecs;
mem::map<lambda *, arc> arcs;
mem::map<bltin, arc> carcs;
fun() : instructions(0), nsecs(0) {}
void addChildTime(node& n) {
if (n.cfunc)
carcs[n.cfunc].add(n);
else
arcs[n.func].add(n);
}
void analyse(node& n) {
instructions += n.instructions;
nsecs += n.nsecs;
size_t numChildren = n.children.size();
for (size_t i = 0; i < numChildren; ++i)
addChildTime(n.children[i]);
}
void dump(ostream& out) {
// The unused line number needed by kcachegrind.
static const string POS = "1";
out << POS << " " << instructions << " " << nsecs << "\n";
for (mem::map<lambda *, arc>::iterator i = arcs.begin();
i != arcs.end();
++i)
{
lambda *l = i->first;
arc& a = i->second;
out << "cfl=" << positionFromLambda(l) << "\n";
out << "cfn=";
printNameFromLambda(out, l);
out << "\n";
out << "calls=" << a.calls << " " << POS << "\n";
out << POS << " " << a.instTotal << " " << a.nsecsTotal << "\n";
}
for (mem::map<bltin, arc>::iterator i = carcs.begin();
i != carcs.end();
++i)
{
bltin b = i->first;
arc& a = i->second;
out << "cfl=C++ code" << endl;
out << "cfn=";
printNameFromBltin(out, b);
out << "\n";
size_t numChildren = n.children.size();
for (size_t i = 0; i < numChildren; ++i)
analyseNode(n.children[i]);
}
// Convert data in the tree of callstack nodes into data for each function.
void analyseData() {
emptynode.computeTotals();
analyseNode(emptynode);
}
// Timing data.
utils::cpuTimer timestamp;
void startLap() {
timestamp.reset();
}
// Called whenever the stack is about to change, in order to record the time
// duration for the current node.
void recordTime() {
topnode().nsecs += (long long) timestamp.nanoseconds(true);
}